<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width">
<meta name="theme-color" content="#222"><meta name="generator" content="Hexo 6.3.0">

  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/images/logo.svg" color="#222">

<link rel="stylesheet" href="/css/main.css">



<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.2.1/css/all.min.css" integrity="sha256-Z1K5uhUaJXA7Ll0XrZ/0JhX4lAtZFpT6jkKrEDT0drU=" crossorigin="anonymous">
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.1.1/animate.min.css" integrity="sha256-PR7ttpcvz8qrF57fur/yAx1qXMFJeJFiA6pSzWi0OIE=" crossorigin="anonymous">

<script class="next-config" data-name="main" type="application/json">{"hostname":"example.com","root":"/","images":"/images","scheme":"Muse","darkmode":false,"version":"8.14.1","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12},"copycode":{"enable":false,"style":null},"bookmark":{"enable":false,"color":"#222","save":"auto"},"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":null,"storage":true,"lazyload":false,"nav":null},"stickytabs":false,"motion":{"enable":true,"async":false,"transition":{"menu_item":"fadeInDown","post_block":"fadeIn","post_header":"fadeInDown","post_body":"fadeInDown","coll_header":"fadeInLeft","sidebar":"fadeInUp"}},"prism":false,"i18n":{"placeholder":"搜索...","empty":"没有找到任何搜索结果：${query}","hits_time":"找到 ${hits} 个搜索结果（用时 ${time} 毫秒）","hits":"找到 ${hits} 个搜索结果"},"path":"/search.xml","localsearch":{"enable":true,"trigger":"auto","top_n_per_article":-1,"unescape":false,"preload":false}}</script><script src="/js/config.js"></script>

    <meta property="og:type" content="website">
<meta property="og:title" content="JsyBlog">
<meta property="og:url" content="http://example.com/page/17/index.html">
<meta property="og:site_name" content="JsyBlog">
<meta property="og:locale" content="zh_CN">
<meta property="article:author" content="SongyangJi">
<meta name="twitter:card" content="summary">


<link rel="canonical" href="http://example.com/page/17/">



<script class="next-config" data-name="page" type="application/json">{"sidebar":"","isHome":true,"isPost":false,"lang":"zh-CN","comments":"","permalink":"","path":"page/17/index.html","title":""}</script>

<script class="next-config" data-name="calendar" type="application/json">""</script>
<title>JsyBlog</title>
  








  <noscript>
    <link rel="stylesheet" href="/css/noscript.css">
  </noscript>
</head>

<body itemscope itemtype="http://schema.org/WebPage" class="use-motion">
  <div class="headband"></div>

  <main class="main">
    <div class="column">
      <header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏" role="button">
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
        <span class="toggle-line"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/" class="brand" rel="start">
      <i class="logo-line"></i>
      <h1 class="site-title">JsyBlog</h1>
      <i class="logo-line"></i>
    </a>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger" aria-label="搜索" role="button">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>



<nav class="site-nav">
  <ul class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a></li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup"><div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off" maxlength="80"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close" role="button">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div class="search-result-container no-result">
  <div class="search-result-icon">
    <i class="fa fa-spinner fa-pulse fa-5x"></i>
  </div>
</div>

    </div>
  </div>

</header>
        
  
  <aside class="sidebar">

    <div class="sidebar-inner sidebar-overview-active">
      <ul class="sidebar-nav">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <div class="sidebar-panel-container">
        <!--noindex-->
        <div class="post-toc-wrap sidebar-panel">
        </div>
        <!--/noindex-->

        <div class="site-overview-wrap sidebar-panel">
          <div class="site-author animated" itemprop="author" itemscope itemtype="http://schema.org/Person">
  <p class="site-author-name" itemprop="name">SongyangJi</p>
  <div class="site-description" itemprop="description"></div>
</div>
<div class="site-state-wrap animated">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
        <a href="/archives/">
          <span class="site-state-item-count">251</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
          <a href="/categories/">
        <span class="site-state-item-count">45</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
          <a href="/tags/">
        <span class="site-state-item-count">109</span>
        <span class="site-state-item-name">标签</span></a>
      </div>
  </nav>
</div>

        </div>
      </div>
    </div>

    
  </aside>


    </div>

    <div class="main-inner index posts-expand">

    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/04/IP/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/04/IP/" class="post-title-link" itemprop="url">IP-互联网协议</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-04 18:34:23" itemprop="dateCreated datePublished" datetime="2021-12-04T18:34:23+08:00">2021-12-04</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-09-30 08:09:11" itemprop="dateModified" datetime="2022-09-30T08:09:11+08:00">2022-09-30</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/" itemprop="url" rel="index"><span itemprop="name">计算机网络</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <p>互联网协议提供了“不可靠的”数据包传输机制（也称“尽力而为”或“尽最大努力交付”）；</p>
<p>也就是说，它不保证数据能准确的传输。数据包在到达的时候可能已经</p>
<p>1.损坏，2.顺序错乱，3.产生冗余包，或者 4.直接丢失。如果应用需要保证可靠性，一般需要采取其他的方法，例如利用IP的上层协议控制。</p>
<p>网络层和传输层协议的区别：网络层协议负责提供主机间的逻辑通信；运输层协议负责提供进程间的逻辑通信。</p>
<blockquote>
<p>UDP和IP的区别</p>
<p>（1）IP是提供主机到主机的服务，如果有多个应用，数据应该交给谁？</p>
<p>IP提供主机到主机的服务，<a target="_blank" rel="noopener" href="https://so.csdn.net/so/search?q=UDP&spm=1001.2101.3001.7020">UDP</a>提供应用到应用的服务，通过端口号进行区分。</p>
<p>（2）IP协议只校验头部，UDP除了头部还校验数据部分。</p>
</blockquote>
<h1 id="IP-协议的定义和作用"><a href="#IP-协议的定义和作用" class="headerlink" title="IP 协议的定义和作用"></a>IP 协议的定义和作用</h1><p>IP 协议（Internet Protocol）又称互联网协议，是支持网间互联的数据包协议。该协议工作在网络层，主要目的就是为了提高网络的可扩展性，和传输层 TCP 相比，IP 协议提供一种无连接&#x2F;不可靠、尽力而为的数据报传输服务，其与TCP协议（传输控制协议）一起构成了TCP&#x2F;IP 协议族的核心。IP 协议主要有以下几个作用：</p>
<ul>
<li><p><strong>寻址和路由</strong>：在IP 数据包中会携带源 IP 地址和目的 IP 地址来标识该数据包的源主机和目的主机。IP 数据报在传输过程中，每个<strong>中间节点（IP 网关、路由器）</strong>只根据网络地址进行转发，如果中间节点是路由器，则路由器会根据路由表选择合适的路径。<strong>IP 协议根据路由选择协议提供的路由信息对 IP 数据报进行转发，直至抵达目的主机</strong>。</p>
</li>
<li><p><strong>分段与重组</strong>：IP 数据包在传输过程中可能会经过不同的网络，<strong>在不同的网络中数据包的最大长度限制是不同的</strong>，IP 协议通过<strong>给每个 IP 数据包分配一个标识符以及分段与组装的相关信息</strong>，<strong>使得数据包在不同的网络中能够传输</strong>，被分段后的 IP 数据报可以独立地在网络中进行转发，在到达目的主机后由目的主机完成重组工作，恢复出原来的 IP 数据包。</p>
</li>
</ul>
<h3 id="路由器和交换机的区别"><a href="#路由器和交换机的区别" class="headerlink" title="路由器和交换机的区别"></a>路由器和交换机的区别</h3><ul>
<li>交换机：交换机用于局域网，利用主机的物理地址（MAC 地址）确定数据转发的目的地址，它工作于数据链路层。</li>
<li>路由器：路由器通过数据包中的目的 IP 地址识别不同的网络从而确定数据转发的目的地址，网络号是唯一的。路由器根据路由选择协议和路由表信息从而确定数据的转发路径，直到到达目的网络，它工作于网络层。</li>
</ul>
<h1 id="IPV4"><a href="#IPV4" class="headerlink" title="IPV4"></a>IPV4</h1><p>Pv4是一种无连接的协议，操作在使用<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E5%88%86%E7%BB%84%E4%BA%A4%E6%8D%A2">分组交换</a>的链路层（如<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E4%BB%A5%E5%A4%AA%E7%BD%91">以太网</a>）上。此协议会尽最大努力交付数据包，意即它不保证任何数据包均能送达目的地，也不保证所有数据包均按照正确的顺序无重复地到达。这些方面是由上层的传输协议（如<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E4%BC%A0%E8%BE%93%E6%8E%A7%E5%88%B6%E5%8D%8F%E8%AE%AE">传输控制协议</a>）处理的。</p>
<h2 id="IP地址"><a href="#IP地址" class="headerlink" title="IP地址"></a>IP地址</h2><p>IP地址最初被描述为包含两部分：网络地址和主机地址。这种区分在IP网络里的路由中使用。</p>
<h3 id="私有IP和公有IP"><a href="#私有IP和公有IP" class="headerlink" title="私有IP和公有IP"></a>私有IP和公有IP</h3><p>IP地址分公有 IP 地址和私有 IP 地址。</p>
<ul>
<li><p>公有地址(Public Address)由Inter NIC（Internet Network Information Center）负责。这些IP地址分配给注册并向Inter NIC提出申请的组织机构。通过它直接访问互联网。</p>
</li>
<li><p>私有IP就是在本地局域网上的IP 与之对应的是公有IP（在互联网上的IP）<br>随着私有IP网络的发展，为节省可分配的注册IP地址，有一组IP地址被拿出来专门用于私有IP网络，称为私有IP地址。</p>
</li>
</ul>
<p>私有ip属于非注册地址，专门为组织机构内部使用。RFC1918定义了私有IP地址范围：</p>
<table class="wikitable">
<tbody><tr>
<th>名字</th>
<th>地址范围</th>
<th>地址数量</th>
<th>有类别的描述</th>
<th>最大的CIDR地址块
</th></tr>
<tr>
<td>24位块</td>
<td>10.0.0.0–10.255.255.255</td>
<td>16,777,216</td>
<td>一个A类</td>
<td>10.0.0.0/8
</td></tr>
<tr>
<td>20位块</td>
<td>172.16.0.0–172.31.255.255</td>
<td>1,048,576</td>
<td>连续的16个B类</td>
<td>172.16.0.0/12
</td></tr>
<tr>
<td>16位块</td>
<td>192.168.0.0–192.168.255.255</td>
<td>65,536</td>
<td>连续的256个C类</td>
<td>192.168.0.0/16
</td></tr></tbody></table>



<p>这些地址是不会被Internet分配的，它们在Internet上也不会被路由，虽然它们不能直接和Internet网连接，但通过技术手段仍旧可以和 Internet通讯（<strong>NAT技术</strong>）。我们可以根据需要来选择适当的地址类，在内部局域网中将这些地址像公用IP地址一样地使用。</p>
<blockquote>
<p> 在Internet上，有些不需要与 Internet通讯的设备，如打印机、可管理集线器集线器)等也可以使用这些地址，以节省IP地址资源。</p>
</blockquote>
<h3 id="IP地址的常规分类"><a href="#IP地址的常规分类" class="headerlink" title="IP地址的常规分类"></a>IP地址的常规分类</h3><blockquote>
<p>此种划分方式早已过时，目前使用的是CIDR（无类域间路由）</p>
</blockquote>
<table class="wikitable mw-collapsible mw-made-collapsible">
<caption>IPv4地址分类
<span class="mw-collapsible-toggle mw-collapsible-toggle-default" role="button" tabindex="0" aria-expanded="true"><a class="mw-collapsible-text">折叠</a></span></caption>
<tbody><tr>
<th>
</th>
<th><b>A类IPv4地址</b>
</th>
<th><b>B类IPv4地址</b>
</th>
<th><b>C类IPv4地址</b>
</th>
<th><b>D类IPv4地址</b>
</th>
<th><b>E类IPv4地址</b>
</th></tr>
<tr>
<th><b>网络标志位</b>
</th>
<td>0
</td>
<td>10
</td>
<td>110
</td>
<td>1110
</td>
<td>11110
</td></tr>
<tr>
<th><b>IP地址范围</b>
</th>
<td>0.0.0.0~127.255.255.255
</td>
<td>128.0.0.0~191.255.255.255
</td>
<td>192.0.0.0~223.255.255.255
</td>
<td>224.0.0.0~239.255.255.255
</td>
<td>240.0.0.0~255.255.255.255
</td></tr>
<tr>
<th><b>可用IP地址范围</b>
</th>
<td>1.0.0.1~127.255.255.254
</td>
<td>128.0.0.1~191.255.255.254
</td>
<td>192.0.0.1~223.255.255.254
</td>
<td>
</td>
<td>
</td></tr>
<tr>
<th><b>是否可以分配给主机使用</b>
</th>
<td>是
</td>
<td>是
</td>
<td>是
</td>
<td>否
</td>
<td>否
</td></tr>
<tr>
<th><b>网络数量（个）</b>
</th>
<td>126   (2<sup>7</sup>-2)
</td>
<td>16384   (2<sup>14</sup>)
</td>
<td>2097152   (2<sup>21</sup>)
</td>
<td>---
</td>
<td>---
</td></tr>
<tr>
<th><b>每个网络中可容纳主机数（个）</b>
</th>
<td>16777214   (2<sup>24</sup>-2)
</td>
<td>65534   (2<sup>16</sup>-2)
</td>
<td>254   (2<sup>8</sup>-2)
</td>
<td>---
</td>
<td>---
</td></tr>
<tr>
<th><b>适用范围</b>
</th>
<td>大量主机的大型网络
</td>
<td>中等规模主机数的网络
</td>
<td>小型局域网
</td>
<td>留给Internet体系结构委员会(IAB)使用
<p><a href="/wiki/%E7%B5%84%E6%92%AD" class="mw-redirect" title="组播">组播</a>地址
</p>
</td>
<td>保留，仅作为搜索、Internet的实验和开发用
</td></tr>
<tr>
<th>备注
</th>
<td>0.0.0.0为特殊地址，表示本网主机
</td>
<td>
</td>
<td>
</td>
<td>
</td>
<td>255.255.255.255为特殊地址，用于定向广播
</td></tr></tbody></table>




<p>IP地址一共分为5类，即A～E，它们分类的依据是其net-id所占的字节长度以及网络号前几位。</p>
<ul>
<li>A类地址:网络号占1个字节。网络号的第一位固定为0。</li>
<li>B类地址：网络号占2个字节。网络号的前两位固定为10。</li>
<li>C类地址：网络号占3个字节。网络号的前三位固定位110。</li>
<li>D类地址：前四位是1110，用于多播(multicast)，即一对多通信。</li>
<li>E类地址：前四位是1111，保留为以后使用。</li>
</ul>
<p>其中，ABC三类地址为单播地址（unicast),用于一对一通信，是最常用的。</p>
<img src="ip-addr.png" style="zoom:45%;" />



<p><strong>说明</strong></p>
<ol>
<li><p>D类与E类IPv4地址不区分网络地址与主机地址</p>
</li>
<li><p>为什么要减 2 呢？因为在 IP 地址中，有两个 IP 是特殊的，分别是主机号全为 1 和 全为 0 地址。</p>
<p><strong>主机号全为 1 指定某个⽹络下的所有主机，⽤于⼴播</strong>;  <strong>主机号全为 0 指定某个⽹络</strong>。</p>
</li>
</ol>
<h3 id="特殊IP地址"><a href="#特殊IP地址" class="headerlink" title="特殊IP地址"></a>特殊IP地址</h3><table class="wikitable">
<caption>特殊IP地址
</caption>
<tbody><tr>
<th>网络号
</th>
<th>主机号
</th>
<th>是否可以作为源地址
</th>
<th>是否可以作为目的地址
</th>
<th>备注/描述
</th></tr>
<tr>
<td>全为0
</td>
<td>全为0
</td>
<td>允许
</td>
<td>禁止
</td>
<td>本网络上的本机（其实，更好的理解是“未指定（即无效的，无意义的）地址”，用作无法使用具体IP情况下的占位符）
</td></tr>
<tr>
<td>全为0
</td>
<td>Host ID
</td>
<td>允许
</td>
<td>禁止
</td>
<td>本地网络的主机（同上）
</td></tr>
<tr>
<td>全为1
</td>
<td>全为1
</td>
<td>禁止
</td>
<td>允许
</td>
<td>在本地网络广播（发到该地址的数据包不能转发到源地址所在网络之外）
</td></tr>
<tr>
<td>127
</td>
<td>任意合法的值
</td>
<td>允许
</td>
<td>允许
</td>
<td>回环地址，用于本地测试（永远都不能出现在主机外部的网络中）
</td></tr>
<tr>
<td>Network ID
</td>
<td>全为1
</td>
<td>禁止
</td>
<td>允许
</td>
<td>在远程网络广播
</td></tr></tbody></table>




<h3 id="CIDR"><a href="#CIDR" class="headerlink" title="CIDR"></a>CIDR</h3><p><strong>无类别域间路由</strong>（Classless Inter-Domain Routing、<strong>CIDR</strong>）是一个用于给用户分配IP地址以及在互联网上有效地路由IP数据包的，对IP地址进行归类的方法。</p>
<p>无类别域间路由是基于<strong>可变长子网掩码（VLSM）</strong>来进行任意长度的前缀的分配的。CIDR包括：</p>
<ul>
<li>指定任意长度的前缀的可变长子网掩码技术。遵从CIDR规则的地址有一个后缀说明<strong>前缀的位数</strong>，例如：192.168.0.0&#x2F;16。这使得对日益缺乏的IPv4地址的使用更加有效。</li>
<li>将多个连续的前缀聚合成超网。以及在互联网中，只要有可能，就显示为一个聚合的网络，因此在总体上可以减少路由表的表项数目。这个过程叫<strong>路由聚合</strong>。</li>
<li>根据机构的实际需要和短期预期需要而不是分类网络中所限定的过大或过小的地址块来管理IP地址的分配的过程。</li>
</ul>
<h3 id="IPV4-地址不够如何解决"><a href="#IPV4-地址不够如何解决" class="headerlink" title="IPV4 地址不够如何解决"></a>IPV4 地址不够如何解决</h3><ol>
<li><strong>DHCP：动态主机配置协议</strong>。动态分配 IP 地址，只给接入网络的设备分配IP地址，因此同一个 MAC 地址的设备，每次接入互联网时，得到的IP地址不一定是相同的，<strong>该协议使得空闲的 IP 地址可以得到充分利用</strong>。</li>
<li><strong>CIDR</strong>：无类别域间路由。CIDR 消除了传统的 A 类、B 类、C 类地址以及划分子网的概念，因而<strong>更加有效的分配 IPv4 的地址空间</strong>，但无法从根本上解决地址耗尽问题。</li>
<li><strong>NAT：网络地址转换协议</strong>。我们知道属于不同局域网的主机可以使用相同的 IP 地址，从而一定程度上缓解了 IP 资源枯竭的问题。然而主机在局域网中使用的 IP 地址是不能在公网中使用的，当局域网主机想要与公网进行通信时， NAT 方法可以将该主机 IP 地址转换成全球 IP 地址。该协议能够有效解决 IP 地址不足的问题。</li>
<li><strong>IPv6</strong> ：作为接替 IPv4 的下一代互联网协议，其可以实现 2 的 128 次方个地址，而这个数量级，即使是给地球上每一颗沙子都分配一个IP地址，该协议能够从根本上解决 IPv4 地址不够用的问题。</li>
</ol>
<h2 id="IP报文结构"><a href="#IP报文结构" class="headerlink" title="IP报文结构"></a>IP报文结构</h2><p>IP报文包含IP首部和数据部分</p>
<p>IPv4报文的首部包含14个字段，其中13个是必须的，第14个是可选的（红色标出），并命名为：“选项”字段。</p>
<p>如下图</p>
<p><img src="/2021/12/04/IP/ipv4.png"></p>
<ul>
<li><p>版本（Version）</p>
<p>版本字段占4bit，通信双方使用的版本必须一致。对于IPv4，字段的值是4。</p>
</li>
<li><p>首部长度（Internet Header Length， IHL）</p>
<p>占4bit，首部长度说明首部有多少32位字（4字节）。由于IPv4首部可能包含数目不定的选项，这个字段也用来确定数据的偏移量。这个字段的最小值是5（二进制0101），相当于5*4&#x3D;20字节（<a target="_blank" rel="noopener" href="https://tools.ietf.org/html/rfc791">RFC 791</a>），最大十进制值是15。</p>
</li>
<li><p>区分服务（Differentiated Services，DS）</p>
<p>占6bit，最初被定义为<strong>服务类型</strong>字段，实际上并未使用，但1998年被IETF重定义为区分服务RFC 2474。只有在使用区分服务时，这个字段才起作用，在一般的情况 下都不使用这个字段。</p>
</li>
<li><p>显式拥塞通告（ Explicit Congestion Notification，ECN）</p>
<p>在RFC 3168中定义，允许在不丢弃报文的同时通知对方网络拥塞的发生。<strong>ECN是一种可选的功能，仅当两端都支持并希望使用，且底层网络支持时才被使用</strong>。</p>
</li>
<li><p>全长（Total Length）</p>
<p>这个16位字段定义了报文总长，包含首部和数据，单位为字节。这个字段的最小值是20（20字节首部+0字节数据），最大值是$2^{16}-1&#x3D;65,535$。<strong>IP规定所有主机都必须支持最小576字节的报文</strong>，这是假定上层数据长度512字节，加上最长IP首部60字节，加上4字节富裕量，得出576字节，但大多数现代主机支持更大的报文。<strong>当下层的数据链路协议的最大传输单元（MTU）字段的值小于IP报文长度时，报文就必须被分片</strong>，详细见下个标题。</p>
</li>
<li><p>标识符（Identification）</p>
<p>占16位，<strong>这个字段主要被用来唯一地标识一个报文的所有分片</strong>，因为分片不一定按序到达，所以在重组时需要知道分片所属的报文。每产生一个数据报，计数器加1，并赋值给此字段。一些实验性的工作建议将此字段用于其它目的，例如增加报文跟踪信息以协助探测伪造的源地址。</p>
</li>
<li><p>标志 （Flags）（<strong>分片相关</strong>）</p>
<p>这个3位字段用于控制和识别分片，它们是：位0：保留，必须为0；位1：<strong>禁止分片</strong>（Don’t Fragment，<strong>DF</strong>），当DF&#x3D;0时才允许分片；位2：<strong>更多分片</strong>（More Fragment，<strong>MF</strong>），MF&#x3D;1代表后面还有分片，MF&#x3D;0 代表已经是最后一个分片。</p>
<p>如果DF标志被设置为1，但路由要求必须分片报文，此报文会被丢弃。这个标志可被用于发往没有能力组装分片的主机。</p>
<p>当一个报文被分片，除了最后一片外的所有分片都设置MF为1。最后一个片段具有非零片段偏移字段，将其与未分片数据包区分开，未分片的偏移字段为0。</p>
</li>
<li><p><strong>分片偏移</strong> （Fragment Offset）</p>
<p>这个13位字段指明了每个分片相对于原始报文开头的偏移量，<strong>以8字节作单位</strong>。</p>
</li>
<li><p>存活时间（Time To Live，TTL）</p>
<p>这个8位字段避免报文在互联网中永远存在（例如陷入路由环路）。存活时间以秒为单位，但小于一秒的时间均向上取整到一秒。<strong>在现实中，这实际上成了一个跳数计数器</strong>：报文经过的每个路由器都将此字段减1，当此字段等于0时，报文不再向下一跳传送并被丢弃，最大值是255。常规地，一份<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/ICMP">ICMP</a>报文被发回到源端说明其发送的报文已被丢弃。这也是<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/Traceroute">traceroute</a>的核心原理（详见ICMP）。</p>
</li>
<li><p>协议 （Protocol）</p>
<p>占8bit，这个字段定义了该报文数据区使用的协议，然后交给对应的传输进程。</p>
</li>
<li><p>首部检验和 （Header Checksum）</p>
<p><strong>这个16位检验和字段，只对首部查错，不包括数据部分</strong>。在每一跳，路由器都要重新计算出的首部检验和并与此字段进行比对，如果不一致，此报文将会被丢弃。重新计算的必要性是因为每一跳的一些首部字段（如TTL、Flag、Offset等）都有可能发生变化，<strong>不检查数据部分是为了减少工作量</strong>。<strong>数据区的错误留待上层协议处理</strong>——用户数据报协议（UDP）和传输控制协议（TCP）都有检验和字段。此处的检验计算方法不使用CRC。</p>
</li>
<li><p>源地址（Source address）</p>
<p>一个IPv4地址由四个字节共32位构成，此字段的值是将每个字节转为二进制并拼在一起所得到的32位值。</p>
<p>例如，10.9.8.7是00001010000010010000100000000111。</p>
<p>但请注意，<strong>因为NAT的存在，这个地址并不总是报文的真实发送端</strong>，因此发往此地址的报文会被送往NAT设备，并由它被翻译为真实的地址。</p>
</li>
<li><p>目的地址（Destination address）<br>与源地址格式相同，但指出报文的接收端。</p>
</li>
<li><p>选项（Options）<br>附加的首部字段可能跟在目的地址之后，但这并不被经常使用，从1到40个字节不等。请注意首部长度字段必须包括足够的32位字来放下所有的选项（首部长度必须能被32位整除，不够就填充）。<strong>IP选项使用的并不多</strong>。</p>
</li>
</ul>
<h2 id="分片和组装"><a href="#分片和组装" class="headerlink" title="分片和组装"></a>分片和组装</h2><p>每种数据链路的最⼤传输单元 MTU 都是不相同的，如 FDDI 数据链路 MTU 4352、以太⽹的 MTU 是 1500 字节</p>
<p>等。</p>
<p>每种数据链路的 MTU 之所以不同，是因为每个不同类型的数据链路的使⽤⽬的不同。使⽤⽬的不同，可承载的</p>
<p>MTU 也就不同。</p>
<p>其中，我们最常⻅数据链路是<strong>以太⽹，它的 MTU 是 1500 字节</strong>。</p>
<p>那么当 IP 数据包⼤⼩⼤于 MTU 时， IP 数据包就会被分⽚。</p>
<p><strong>经过分⽚之后的 IP 数据报在被重组的时候，只能由⽬标主机进⾏，路由器是不会进⾏重组的</strong>。</p>
<blockquote>
<p>（详见 P334 分段和重组的两种方式）</p>
</blockquote>
<p>假设发送⽅发送⼀个 4000 字节的⼤数据报，若要传输在以太⽹链路，则需要把数据报分⽚成 3 个⼩数据报进⾏传</p>
<p>输，再交由接收⽅重组成⼤数据报。</p>
<p>在分⽚传输中，⼀旦某个分⽚丢失，则会造成整个 IP 数据报作废，<strong>所以 TCP 引⼊了 MSS 也就是在 TCP 层进⾏分⽚不由 IP 层分⽚</strong>，那么对于 UDP 我们尽量不要发送⼀个⼤于 MTU 的数据报⽂。</p>
<blockquote>
<p>详见 ”路径 MTU 发现“，避免IP分组。</p>
</blockquote>
<h3 id="分组"><a href="#分组" class="headerlink" title="分组"></a>分组</h3><p>当设备收到IP报文时，分析其目的地址并决定要在哪个链路上发送它。<strong>MTU决定了数据载荷的最大长度，如IP报文长度比MTU大，则IP数据包必须进行分片。每一片的长度都小于等于MTU减去IP首部长度</strong>。接下来每一片均被放到独立的IP报文中，并进行如下修改：</p>
<ul>
<li>总长字段被修改为此分片的长度；</li>
<li>更多分片（MF）标志被设置，除了最后一片；</li>
<li>分片偏移量字段被调整为合适的值；</li>
<li>首部检验和被重新计算。</li>
</ul>
<p>例如，对于一个长20字节的首部和一个MTU为1,500的以太网，分片偏移量将会是：0、(1480&#x2F;8)&#x3D;185、(2960&#x2F;8)&#x3D;370、(4440&#x2F;8)&#x3D;555、(5920&#x2F;8)&#x3D;740、等等。</p>
<p>比如，一个4,500字节的数据载荷被封装进了一个没有选项的IP报文（即总长为4,520字节），并在MTU为2,500字节的链路上传输，那么它会被破成如下两个分片：</p>
<table>
<tbody><tr>
<th rowspan="2">#
</th>
<th colspan="2" width="200">总长
</th>
<th rowspan="2">更多分片（MF）？
</th>
<th rowspan="2">DF
</th>
<th rowspan="2">分片偏移量
</th></tr>
<tr>
<th width="100">首部
</th>
<th width="100">数据
</th></tr>
<tr>
<td rowspan="2">1</td>
<td colspan="2">2500</td>
<td rowspan="2" class="table-yes" style="text-align:center; background:#90FF90">是
</td>
<td rowspan="2">0</td>
<td rowspan="2">0
</td></tr>
<tr>
<td>20</td>
<td>2480
</td></tr>
<tr>
<td rowspan="2">2</td>
<td colspan="2">2040</td>
<td rowspan="2" class="table-no" style="text-align:center; background:#FF9090">否
</td>
<td rowspan="2">0</td>
<td rowspan="2">310
</td></tr>
<tr>
<td>20</td>
<td>2020
</td></tr></tbody></table>





<h3 id="重组"><a href="#重组" class="headerlink" title="重组"></a>重组</h3><p>当一个接收者发现IP报文的下列项目之一为真时：</p>
<ul>
<li>DF标志为0（也就是允许分段）；</li>
<li>分片偏移量字段不为0。<br>它便知道这个报文已被分片，并随即将数据、标识符字段、分片偏移量和更多分片标志一起储存起来。</li>
</ul>
<p>当接受者收到了更多分片标志未被设置的分片时，它便知道原始数据载荷的总长。</p>
<p>一旦它收齐了所有的分片，它便可以将所有片按照正确的顺序（通过分片偏移量）组装起来，并交给上层协议栈。</p>
<blockquote>
<p>参考 </p>
<p><a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E7%BD%91%E9%99%85%E5%8D%8F%E8%AE%AE">网际协议</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/03/%E5%89%91%E6%8C%87Offer%E9%A2%98%E8%A7%A3-1/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/03/%E5%89%91%E6%8C%87Offer%E9%A2%98%E8%A7%A3-1/" class="post-title-link" itemprop="url">剑指Offer题解-1</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-03 14:29:19" itemprop="dateCreated datePublished" datetime="2021-12-03T14:29:19+08:00">2021-12-03</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2023-01-03 20:24:37" itemprop="dateModified" datetime="2023-01-03T20:24:37+08:00">2023-01-03</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E7%AE%97%E6%B3%95%E9%A2%98/" itemprop="url" rel="index"><span itemprop="name">算法题</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h4 id="剑指-Offer-09-用两个栈实现队列"><a href="#剑指-Offer-09-用两个栈实现队列" class="headerlink" title="剑指 Offer 09. 用两个栈实现队列"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/yong-liang-ge-zhan-shi-xian-dui-lie-lcof/">剑指 Offer 09. 用两个栈实现队列</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">CQueue</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    stack&lt;<span class="type">int</span>&gt; st1,st2;</span><br><span class="line">    <span class="built_in">CQueue</span>() &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">appendTail</span><span class="params">(<span class="type">int</span> value)</span> </span>&#123;</span><br><span class="line">        st1.<span class="built_in">push</span>(value);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">deleteHead</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(st1.<span class="built_in">size</span>() + st2.<span class="built_in">size</span>() == <span class="number">0</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">        <span class="comment">// 将 st1 其中的元素全都取出来的，顺序就是FIFO的顺序了</span></span><br><span class="line">        <span class="keyword">if</span>(st2.<span class="built_in">empty</span>())&#123;</span><br><span class="line">            <span class="keyword">while</span>(!st1.<span class="built_in">empty</span>())&#123;</span><br><span class="line">                <span class="type">int</span> val = st1.<span class="built_in">top</span>();</span><br><span class="line">                st1.<span class="built_in">pop</span>();</span><br><span class="line">                st2.<span class="built_in">push</span>(val);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> val = st2.<span class="built_in">top</span>();</span><br><span class="line">        st2.<span class="built_in">pop</span>();</span><br><span class="line">        <span class="keyword">return</span> val;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h4 id="剑指-Offer-10-I-斐波那契数列"><a href="#剑指-Offer-10-I-斐波那契数列" class="headerlink" title="剑指 Offer 10- I. 斐波那契数列"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/fei-bo-na-qi-shu-lie-lcof/">剑指 Offer 10- I. 斐波那契数列</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="type">int</span> mod = <span class="number">1e9</span>+<span class="number">7</span>;</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">fib</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(n == <span class="number">0</span>) <span class="keyword">return</span> - <span class="number">1</span>;</span><br><span class="line">        <span class="type">int</span> a = <span class="number">0</span>, b = <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; n - <span class="number">1</span>; i++) &#123;</span><br><span class="line">            <span class="type">int</span> c = (a + b) % mod;</span><br><span class="line">            a = b;</span><br><span class="line">            b = c;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> b;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<p><strong>矩阵加速</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="type">int</span> <span class="variable">mod</span> <span class="operator">=</span> (<span class="type">int</span>)<span class="number">1e9</span> + <span class="number">7</span>;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">fib</span><span class="params">(<span class="type">int</span> n)</span> &#123;</span><br><span class="line">        <span class="type">int</span>[][] A = &#123;&#123;<span class="number">1</span>, <span class="number">1</span>&#125;, &#123;<span class="number">1</span>, <span class="number">0</span>&#125;&#125;;</span><br><span class="line">        <span class="type">int</span>[][] B = pow(A, n);</span><br><span class="line">        <span class="keyword">return</span> B[<span class="number">1</span>][<span class="number">0</span>];</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 矩阵乘法，返回新数组</span></span><br><span class="line">    <span class="type">int</span>[][] mul(<span class="type">int</span>[][] A, <span class="type">int</span>[][] B) &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">m</span> <span class="operator">=</span> A.length, n = B[<span class="number">0</span>].length;</span><br><span class="line">        <span class="type">int</span> <span class="variable">len</span> <span class="operator">=</span> A[<span class="number">0</span>].length;</span><br><span class="line">        <span class="type">int</span>[][] ans = <span class="keyword">new</span> <span class="title class_">int</span>[m][n];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; m; i++) &#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> <span class="number">0</span>; j &lt; n; j++) &#123;</span><br><span class="line">                <span class="type">int</span> <span class="variable">res</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">                <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">k</span> <span class="operator">=</span> <span class="number">0</span>; k &lt; len; k++) &#123;</span><br><span class="line">                    <span class="comment">// 小心溢出</span></span><br><span class="line">                    res = (<span class="type">int</span>)((res + (<span class="type">long</span>)A[i][k] * B[k][j] ) % mod);</span><br><span class="line">                &#125;</span><br><span class="line">                ans[i][j] = res;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 快速幂计算</span></span><br><span class="line">    <span class="type">int</span>[][] pow(<span class="type">int</span>[][] A, <span class="type">int</span> n) &#123;</span><br><span class="line">        <span class="type">int</span>[][] ans = <span class="keyword">new</span> <span class="title class_">int</span>[A.length][A[<span class="number">0</span>].length];</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; ans.length; i++) &#123;</span><br><span class="line">            ans[i][i] = <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">while</span>(n &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span>(n % <span class="number">2</span> == <span class="number">1</span>) &#123;</span><br><span class="line">                ans = mul(ans, A);</span><br><span class="line">            &#125;</span><br><span class="line">            A = mul(A, A);</span><br><span class="line">            n &gt;&gt;= <span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-03-数组中重复的数字"><a href="#剑指-Offer-03-数组中重复的数字" class="headerlink" title="剑指 Offer 03. 数组中重复的数字"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/shu-zu-zhong-zhong-fu-de-shu-zi-lcof/">剑指 Offer 03. 数组中重复的数字</a></h4><p>unfinished</p>
<p>如何做到<code>O(1)</code>?。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">findRepeatNumber</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">        <span class="comment">// 每次要么做到 nums[i] == i， 要么做到 nums[nums[i]] == nums[i]</span></span><br><span class="line">        <span class="comment">// 也就是说，每次都有一个元素被放到正确的地方。</span></span><br><span class="line">        <span class="comment">// 可以想想为什么是 O(n) 的</span></span><br><span class="line">        <span class="keyword">while</span>(i &lt; nums.<span class="built_in">size</span>()) &#123;</span><br><span class="line">            <span class="keyword">if</span>(nums[i] == i) &#123;</span><br><span class="line">                i++;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span>(nums[nums[i]] == nums[i]) <span class="keyword">return</span> nums[i]; <span class="comment">// 实际上也避免了死循环</span></span><br><span class="line">            <span class="built_in">swap</span>(nums[i],nums[nums[i]]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>







<h4 id="剑指-Offer-04-二维数组中的查找"><a href="#剑指-Offer-04-二维数组中的查找" class="headerlink" title="剑指 Offer 04. 二维数组中的查找"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/er-wei-shu-zu-zhong-de-cha-zhao-lcof/">剑指 Offer 04. 二维数组中的查找</a></h4><p><strong>单调的思维</strong> ，一句话解释，每一行的最后一个小于等于 target的元素的位置是单调的。</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">findNumberIn2DArray</span><span class="params">(vector&lt;vector&lt;<span class="type">int</span>&gt;&gt;&amp; matrix, <span class="type">int</span> target)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> m = matrix.<span class="built_in">size</span>();</span><br><span class="line">        <span class="keyword">if</span>(m == <span class="number">0</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="type">int</span> n = matrix[<span class="number">0</span>].<span class="built_in">size</span>();</span><br><span class="line">        <span class="comment">// 从右上角进行搜索</span></span><br><span class="line">        <span class="type">int</span> j = n - <span class="number">1</span> , i = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span>(j &gt;= <span class="number">0</span> &amp;&amp; i &lt; m) &#123;</span><br><span class="line">            <span class="comment">// 移动到第一个小于等于 target的地方</span></span><br><span class="line">            <span class="keyword">while</span>(j &gt;= <span class="number">0</span> &amp;&amp; matrix[i][j] &gt; target) j--;</span><br><span class="line">            <span class="comment">// 检查</span></span><br><span class="line">            <span class="keyword">if</span>(j &gt;= <span class="number">0</span> &amp;&amp; matrix[i][j] == target) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">            <span class="comment">// 此时 matrix[i][j]是小于 target的</span></span><br><span class="line">            i++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h4 id="剑指-Offer-11-旋转数组的最小数字"><a href="#剑指-Offer-11-旋转数组的最小数字" class="headerlink" title="剑指 Offer 11. 旋转数组的最小数字"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/xuan-zhuan-shu-zu-de-zui-xiao-shu-zi-lcof/">剑指 Offer 11. 旋转数组的最小数字</a></h4><p>如果没有重复元素的话：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">findMin</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> n = nums.<span class="built_in">size</span>();</span><br><span class="line">        <span class="type">int</span> l = <span class="number">0</span>, r = n - <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">while</span>(l &lt; r) &#123;</span><br><span class="line">            <span class="type">int</span> mid = l + (r - l)/<span class="number">2</span>;</span><br><span class="line">            <span class="comment">// 判断 nums[mid]在那一段</span></span><br><span class="line">            <span class="keyword">if</span>(nums[mid] &lt; nums[r]) &#123;</span><br><span class="line">                r = mid;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                l = mid + <span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> nums[l];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>







<h4 id="剑指-Offer-07-重建二叉树"><a href="#剑指-Offer-07-重建二叉树" class="headerlink" title="剑指 Offer 07. 重建二叉树"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/zhong-jian-er-cha-shu-lcof/">剑指 Offer 07. 重建二叉树</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">TreeNode* <span class="title">buildTree</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; preorder, vector&lt;<span class="type">int</span>&gt;&amp; inorder)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> n = preorder.<span class="built_in">size</span>();</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">create</span>(<span class="number">0</span>, n - <span class="number">1</span>, <span class="number">0</span>, n - <span class="number">1</span>, preorder, inorder);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 返回某子树的根</span></span><br><span class="line">    <span class="function">TreeNode* <span class="title">create</span><span class="params">(<span class="type">int</span> preL, <span class="type">int</span> preR, <span class="type">int</span> inL, <span class="type">int</span> inR,vector&lt;<span class="type">int</span>&gt;&amp; preorder, vector&lt;<span class="type">int</span>&gt;&amp; inorder)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(preL &gt; preR)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> val  = preorder[preL]; <span class="comment">// 首个元素就是 根 root</span></span><br><span class="line">        TreeNode* node = <span class="keyword">new</span> <span class="built_in">TreeNode</span>(val);</span><br><span class="line">        <span class="comment">// 在中序遍历找出 根</span></span><br><span class="line">        <span class="type">int</span> idx = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = inL; i &lt;= inR; i++)&#123;</span><br><span class="line">            <span class="keyword">if</span>(inorder[i] == val)&#123;</span><br><span class="line">                idx = i;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="type">int</span> numLeft = idx - inL; <span class="comment">// 左子树节点的数量</span></span><br><span class="line">        node-&gt;left = <span class="built_in">create</span>(preL + <span class="number">1</span>, preL + numLeft, inL, idx - <span class="number">1</span>, preorder, inorder);</span><br><span class="line">        node-&gt;right = <span class="built_in">create</span>(preL + numLeft + <span class="number">1</span>, preR, idx + <span class="number">1</span>, inR, preorder, inorder);</span><br><span class="line">        <span class="keyword">return</span> node;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-14-I-剪绳子"><a href="#剑指-Offer-14-I-剪绳子" class="headerlink" title="剑指 Offer 14- I. 剪绳子"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/jian-sheng-zi-lcof/">剑指 Offer 14- I. 剪绳子</a></h4><p>简单DP</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">cuttingRope</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">        <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">dp</span><span class="params">(n+<span class="number">1</span>)</span></span>;</span><br><span class="line">        dp[<span class="number">1</span>] = <span class="number">1</span>;</span><br><span class="line">        <span class="comment">// 注意提给的限制，就是 段数大于 1 </span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">2</span>;i &lt;= n; i++) &#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> j = <span class="number">1</span>; j &lt; i; j++) &#123;</span><br><span class="line">                <span class="comment">// max(dp[j], j) 分别对应切割的最大值 和 不切割的情况</span></span><br><span class="line">                dp[i] = <span class="built_in">max</span>(dp[i], <span class="built_in">max</span>(dp[j], j) * (i - j));</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> dp[n];</span><br><span class="line">    &#125;  </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<p>数学求导推导：</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="type">int</span> <span class="title">cuttingRope</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (n &lt;= <span class="number">3</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> n<span class="number">-1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">            据 数学证明：</span></span><br><span class="line"><span class="comment">            当 n &gt;= 3时，因数 拆分成 3 的计算结果 较大</span></span><br><span class="line"><span class="comment">            当 n &lt; 3时，因数越大，计算结果越大</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line">        <span class="type">int</span> mod = <span class="number">1000000007</span>;</span><br><span class="line">        <span class="type">long</span> result = <span class="number">1L</span>;</span><br><span class="line">        <span class="keyword">while</span> (n &gt; <span class="number">4</span>) &#123;</span><br><span class="line">            result = result * <span class="number">3</span> % mod;</span><br><span class="line">            n -= <span class="number">3</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/*</span></span><br><span class="line"><span class="comment">            将 最后一个 因数 计算在内</span></span><br><span class="line"><span class="comment">        */</span></span><br><span class="line">        result = result * n % mod;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> (<span class="type">int</span>)result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h4 id="剑指-Offer-12-矩阵中的路径"><a href="#剑指-Offer-12-矩阵中的路径" class="headerlink" title="剑指 Offer 12. 矩阵中的路径"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/ju-zhen-zhong-de-lu-jing-lcof/">剑指 Offer 12. 矩阵中的路径</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">const</span> <span class="type">int</span> dx[] = &#123;<span class="number">1</span>, <span class="number">0</span>, <span class="number">-1</span>, <span class="number">0</span>&#125;;</span><br><span class="line"><span class="type">const</span> <span class="type">int</span> dy[] = &#123;<span class="number">0</span>, <span class="number">1</span>, <span class="number">0</span>, <span class="number">-1</span>&#125;;</span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="type">bool</span> ans = <span class="literal">false</span>;</span><br><span class="line">    string s;</span><br><span class="line">    vector&lt;vector&lt;<span class="type">char</span>&gt;&gt; g;</span><br><span class="line">    <span class="type">int</span> m, n;</span><br><span class="line">    <span class="type">bool</span> vis[<span class="number">210</span>][<span class="number">210</span>];</span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">exist</span><span class="params">(vector&lt;vector&lt;<span class="type">char</span>&gt;&gt;&amp; board, string word)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(word == <span class="string">&quot;&quot;</span>) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        s = word;</span><br><span class="line">        g = board;</span><br><span class="line">        m = g.<span class="built_in">size</span>();</span><br><span class="line">        n = g[<span class="number">0</span>].<span class="built_in">size</span>();</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; m; i++) &#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> j = <span class="number">0</span>; j &lt; n; j++) &#123;</span><br><span class="line">                <span class="built_in">memset</span>(vis, <span class="number">0</span>, <span class="built_in">sizeof</span>(vis));</span><br><span class="line">                <span class="built_in">dfs</span>(i, j, <span class="number">0</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> i, <span class="type">int</span> j, <span class="type">int</span> pos)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(pos == s.<span class="built_in">size</span>()) &#123;</span><br><span class="line">            ans = <span class="literal">true</span>;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(s[pos] != g[i][j]) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(pos == s.<span class="built_in">size</span>() - <span class="number">1</span>) &#123;</span><br><span class="line">            ans = <span class="literal">true</span>;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125; <span class="comment">//特判，走到头了，无需再走</span></span><br><span class="line"></span><br><span class="line">        vis[i][j] = <span class="number">1</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> k = <span class="number">0</span>; k &lt; <span class="number">4</span>; k++) &#123;</span><br><span class="line">            <span class="type">int</span> x = i + dx[k];</span><br><span class="line">            <span class="type">int</span> y = j + dy[k];</span><br><span class="line">            <span class="keyword">if</span>(x &gt;= <span class="number">0</span> &amp;&amp; x &lt; m &amp;&amp; y &gt;= <span class="number">0</span> &amp;&amp; y &lt; n &amp;&amp; !vis[x][y]) &#123;</span><br><span class="line">                <span class="keyword">if</span>(<span class="built_in">dfs</span>(x, y, pos + <span class="number">1</span>)) &#123;</span><br><span class="line">                    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        vis[i][j] = <span class="number">0</span>; <span class="comment">//回溯</span></span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<p>时间复杂度的上限：$O*(M<em>N</em>3^L)$</p>
<h4 id="剑指-Offer-25-合并两个排序的链表"><a href="#剑指-Offer-25-合并两个排序的链表" class="headerlink" title="剑指 Offer 25. 合并两个排序的链表"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/he-bing-liang-ge-pai-xu-de-lian-biao-lcof/">剑指 Offer 25. 合并两个排序的链表</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * struct ListNode &#123;</span></span><br><span class="line"><span class="comment"> *     int val;</span></span><br><span class="line"><span class="comment"> *     ListNode *next;</span></span><br><span class="line"><span class="comment"> *     ListNode(int x) : val(x), next(NULL) &#123;&#125;</span></span><br><span class="line"><span class="comment"> * &#125;;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">ListNode* <span class="title">mergeTwoLists</span><span class="params">(ListNode* l1, ListNode* l2)</span> </span>&#123;</span><br><span class="line">        ListNode* dum = <span class="keyword">new</span> <span class="built_in">ListNode</span>(<span class="number">-1</span>), *cur = dum;</span><br><span class="line">        ListNode *p1 = l1, *p2 = l2;</span><br><span class="line">        <span class="keyword">while</span>(p1 &amp;&amp; p2) &#123;</span><br><span class="line">            <span class="keyword">if</span>(p1-&gt;val &lt; p2-&gt;val) &#123;</span><br><span class="line">                cur-&gt;next = p1;</span><br><span class="line">                p1 = p1-&gt;next; </span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                cur-&gt;next = p2;</span><br><span class="line">                p2 = p2-&gt;next;</span><br><span class="line">            &#125;</span><br><span class="line">            cur = cur-&gt;next;</span><br><span class="line">        &#125;</span><br><span class="line">        cur-&gt;next = p1 ? p1 : p2;</span><br><span class="line">        <span class="keyword">return</span> dum-&gt;next;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-26-树的子结构"><a href="#剑指-Offer-26-树的子结构" class="headerlink" title="剑指 Offer 26. 树的子结构"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/shu-de-zi-jie-gou-lcof/">剑指 Offer 26. 树的子结构</a></h4><p>unfinished</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">isSubStructure</span><span class="params">(TreeNode* A, TreeNode* B)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(!B || !A) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">check</span>(A, B)) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(<span class="built_in">isSubStructure</span>(A-&gt;left,B) || <span class="built_in">isSubStructure</span>(A-&gt;right,B))&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 检查 a 是否包含 b, 必须各自以 a、b 为根</span></span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">check</span><span class="params">(TreeNode* a,TreeNode* b)</span></span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(!b)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(!a)&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> a-&gt;val==b-&gt;val &amp;&amp; <span class="built_in">check</span>(a-&gt;left,b-&gt;left) &amp;&amp; <span class="built_in">check</span>(a-&gt;right,b-&gt;right);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h4 id="剑指-Offer-27-二叉树的镜像"><a href="#剑指-Offer-27-二叉树的镜像" class="headerlink" title="剑指 Offer 27. 二叉树的镜像"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/er-cha-shu-de-jing-xiang-lcof/">剑指 Offer 27. 二叉树的镜像</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">TreeNode* <span class="title">mirrorTree</span><span class="params">(TreeNode* root)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(root == <span class="literal">nullptr</span>) <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">        <span class="built_in">swap</span>(root-&gt;left, root-&gt;right);</span><br><span class="line">        <span class="built_in">mirrorTree</span>(root-&gt;left);</span><br><span class="line">        <span class="built_in">mirrorTree</span>(root-&gt;right);</span><br><span class="line">        <span class="keyword">return</span> root;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-28-对称的二叉树"><a href="#剑指-Offer-28-对称的二叉树" class="headerlink" title="剑指 Offer 28. 对称的二叉树"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/dui-cheng-de-er-cha-shu-lcof/">剑指 Offer 28. 对称的二叉树</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for a binary tree node.</span></span><br><span class="line"><span class="comment"> * struct TreeNode &#123;</span></span><br><span class="line"><span class="comment"> *     int val;</span></span><br><span class="line"><span class="comment"> *     TreeNode *left;</span></span><br><span class="line"><span class="comment"> *     TreeNode *right;</span></span><br><span class="line"><span class="comment"> *     TreeNode(int x) : val(x), left(NULL), right(NULL) &#123;&#125;</span></span><br><span class="line"><span class="comment"> * &#125;;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">isSymmetric</span><span class="params">(TreeNode* root)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="built_in">check</span>(root, root);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">check</span><span class="params">(TreeNode* a, TreeNode* b)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(a == <span class="literal">nullptr</span> &amp;&amp; a == <span class="literal">nullptr</span>) <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">if</span>((a &amp;&amp; !b) || (!a &amp;&amp; b)) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">        <span class="keyword">return</span> a-&gt;val == b-&gt;val &amp;&amp; <span class="built_in">check</span>(a-&gt;left, b-&gt;right) &amp;&amp; <span class="built_in">check</span>(a-&gt;right, b-&gt;left);  </span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-21-调整数组顺序使奇数位于偶数前面"><a href="#剑指-Offer-21-调整数组顺序使奇数位于偶数前面" class="headerlink" title="剑指 Offer 21. 调整数组顺序使奇数位于偶数前面"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/diao-zheng-shu-zu-shun-xu-shi-qi-shu-wei-yu-ou-shu-qian-mian-lcof/">剑指 Offer 21. 调整数组顺序使奇数位于偶数前面</a></h4><p>循环不变量</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">exchange</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// [0 , i] [i + 1,..]</span></span><br><span class="line">        <span class="type">int</span> i = <span class="number">-1</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> j = <span class="number">0</span>; j &lt; nums.<span class="built_in">size</span>(); j++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(nums[j]&amp;<span class="number">1</span>) &#123;</span><br><span class="line">                <span class="built_in">swap</span>(nums[++i], nums[j]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> nums;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>







<h4 id="剑指-Offer-29-顺时针打印矩阵"><a href="#剑指-Offer-29-顺时针打印矩阵" class="headerlink" title="剑指 Offer 29. 顺时针打印矩阵"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/shun-shi-zhen-da-yin-ju-zhen-lcof/">剑指 Offer 29. 顺时针打印矩阵</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">spiralOrder</span><span class="params">(vector&lt;vector&lt;<span class="type">int</span>&gt;&gt;&amp; g)</span> </span>&#123;</span><br><span class="line">        vector&lt;<span class="type">int</span>&gt; ans;</span><br><span class="line">        <span class="type">int</span> m = g.<span class="built_in">size</span>();</span><br><span class="line">        <span class="keyword">if</span>(m == <span class="number">0</span>) <span class="keyword">return</span> ans;</span><br><span class="line">        <span class="type">int</span> n = g[<span class="number">0</span>].<span class="built_in">size</span>();</span><br><span class="line">        <span class="type">int</span> cnt = <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> left = <span class="number">0</span>, right = n - <span class="number">1</span>, high = <span class="number">0</span>, low = m - <span class="number">1</span>; </span><br><span class="line">        <span class="keyword">while</span>(cnt &lt; m * n) &#123;</span><br><span class="line">            <span class="comment">// 注意对 cnt 的限制</span></span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> j = left; j &lt;= right &amp;&amp; cnt &lt; m * n; j++) &#123;</span><br><span class="line">                ans.<span class="built_in">push_back</span>(g[high][j]);</span><br><span class="line">                ++cnt;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> i = high + <span class="number">1</span>; i &lt;= low &amp;&amp; cnt &lt; m * n; i++) &#123;</span><br><span class="line">                ans.<span class="built_in">push_back</span>(g[i][right]);</span><br><span class="line">                ++cnt;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> j = right - <span class="number">1</span>; j &gt;= left &amp;&amp; cnt &lt; m * n; j--) &#123;</span><br><span class="line">                ans.<span class="built_in">push_back</span>(g[low][j]);</span><br><span class="line">                ++cnt;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> i = low - <span class="number">1</span>; i &gt;= high + <span class="number">1</span> &amp;&amp; cnt &lt; m * n; i--) &#123;</span><br><span class="line">                ans.<span class="built_in">push_back</span>(g[i][left]);  </span><br><span class="line">                ++cnt;</span><br><span class="line">            &#125;</span><br><span class="line">            ++left;</span><br><span class="line">            --right;</span><br><span class="line">            ++high;</span><br><span class="line">            --low;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-19-正则表达式匹配"><a href="#剑指-Offer-19-正则表达式匹配" class="headerlink" title="剑指 Offer 19. 正则表达式匹配"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/zheng-ze-biao-da-shi-pi-pei-lcof/">剑指 Offer 19. 正则表达式匹配</a></h4><p>unfinished</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="type">bool</span> f[<span class="number">30</span>][<span class="number">40</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">isMatch</span><span class="params">(string s, string p)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> m = s.<span class="built_in">size</span>(), n = p.<span class="built_in">size</span>();</span><br><span class="line">        s = <span class="string">&quot; &quot;</span> + s;</span><br><span class="line">        p = <span class="string">&quot; &quot;</span> + p;</span><br><span class="line">        f[<span class="number">0</span>][<span class="number">0</span>] = <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">auto</span> ok = [&amp;](<span class="type">int</span> i, <span class="type">int</span> j) &#123;</span><br><span class="line">            <span class="keyword">if</span>(i == <span class="number">0</span> || j == <span class="number">0</span>) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">            <span class="keyword">return</span> p[j] == <span class="string">&#x27;.&#x27;</span> || s[i] == p[j];</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt;= m; i++) &#123;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> j = <span class="number">1</span>; j &lt;= n; j++) &#123;</span><br><span class="line">                <span class="keyword">if</span>(p[j] == <span class="string">&#x27;*&#x27;</span>) &#123;</span><br><span class="line">                    f[i][j] = f[i][j - <span class="number">2</span>]; <span class="comment">// 题给数据保证不越界</span></span><br><span class="line">                    <span class="keyword">if</span>(<span class="built_in">ok</span>(i, j - <span class="number">1</span>)) &#123;</span><br><span class="line">                        f[i][j] |= f[i - <span class="number">1</span>][j];</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    f[i][j] = <span class="built_in">ok</span>(i, j) ? f[i - <span class="number">1</span>][j - <span class="number">1</span>] : <span class="literal">false</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> f[m][n];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-17-打印从1到最大的n位数"><a href="#剑指-Offer-17-打印从1到最大的n位数" class="headerlink" title="剑指 Offer 17. 打印从1到最大的n位数"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/da-yin-cong-1dao-zui-da-de-nwei-shu-lcof/">剑指 Offer 17. 打印从1到最大的n位数</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">printNumbers</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">        string s = <span class="string">&quot;1&quot;</span> , x = <span class="string">&quot;0&quot;</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; i++) s += <span class="string">&quot;0&quot;</span>, x+= <span class="string">&quot;0&quot;</span>;</span><br><span class="line">        <span class="built_in">incr</span>(x);</span><br><span class="line">        vector&lt;<span class="type">int</span>&gt; ans;</span><br><span class="line">        <span class="keyword">while</span>(x &lt; s)&#123;</span><br><span class="line">            ans.<span class="built_in">push_back</span>(<span class="built_in">stoi</span>(x));</span><br><span class="line">            <span class="built_in">incr</span>(x);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 去除前导 0 </span></span><br><span class="line">    <span class="function">string <span class="title">pretty</span><span class="params">(string &amp;s)</span></span>&#123;</span><br><span class="line">        <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span>(i&lt;s.<span class="built_in">size</span>() &amp;&amp; s[i] == <span class="string">&#x27;0&#x27;</span>) i++;</span><br><span class="line">        <span class="keyword">if</span>(i == s.<span class="built_in">size</span>())&#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="string">&quot;0&quot;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> s.<span class="built_in">substr</span>(i);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">incr</span><span class="params">(string &amp;s)</span></span>&#123;</span><br><span class="line">        <span class="type">int</span> j = s.<span class="built_in">size</span>() - <span class="number">1</span>;</span><br><span class="line">        <span class="comment">// 子串的低位 对应 数字的高位</span></span><br><span class="line">        <span class="keyword">while</span>(j &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="type">int</span> cnt = s[j] - <span class="string">&#x27;0&#x27;</span> + <span class="number">1</span>; <span class="comment">// 既有可能一开始加的 1 ，也要可能是进位上来的 1 </span></span><br><span class="line">            <span class="keyword">if</span>(cnt &lt; <span class="number">10</span>)&#123;</span><br><span class="line">                ++s[j]; <span class="comment">// 字符的增加</span></span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            s[j--] = <span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-24-反转链表"><a href="#剑指-Offer-24-反转链表" class="headerlink" title="剑指 Offer 24. 反转链表"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/fan-zhuan-lian-biao-lcof/">剑指 Offer 24. 反转链表</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * struct ListNode &#123;</span></span><br><span class="line"><span class="comment"> *     int val;</span></span><br><span class="line"><span class="comment"> *     ListNode *next;</span></span><br><span class="line"><span class="comment"> *     ListNode(int x) : val(x), next(NULL) &#123;&#125;</span></span><br><span class="line"><span class="comment"> * &#125;;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function">ListNode* <span class="title">reverseList</span><span class="params">(ListNode* head)</span> </span>&#123;</span><br><span class="line">        ListNode* res = <span class="literal">NULL</span>;</span><br><span class="line">        ListNode* p = head;</span><br><span class="line">        <span class="keyword">while</span>(p) &#123;</span><br><span class="line">            <span class="keyword">auto</span> q = p-&gt;next;</span><br><span class="line">            p-&gt;next = res;</span><br><span class="line">            res = p;</span><br><span class="line">            p = q;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> res;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-30-包含min函数的栈"><a href="#剑指-Offer-30-包含min函数的栈" class="headerlink" title="剑指 Offer 30. 包含min函数的栈"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/bao-han-minhan-shu-de-zhan-lcof/">剑指 Offer 30. 包含min函数的栈</a></h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MinStack</span> &#123;</span><br><span class="line"></span><br><span class="line">  </span><br><span class="line">  	<span class="comment">// min是一个单调减的栈</span></span><br><span class="line">    Deque&lt;Integer&gt; st = <span class="keyword">new</span> <span class="title class_">ArrayDeque</span>&lt;Integer&gt;(), min = <span class="keyword">new</span> <span class="title class_">ArrayDeque</span>&lt;Integer&gt;(); </span><br><span class="line">  	</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MinStack</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">push</span><span class="params">(<span class="type">int</span> x)</span> &#123;</span><br><span class="line">        st.push(x);</span><br><span class="line">        <span class="keyword">if</span>(min.isEmpty()|| min.peek() &gt;= x) &#123;</span><br><span class="line">            min.push(x);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">pop</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> st.pop();</span><br><span class="line">        <span class="keyword">if</span>(x == min.peek()) &#123;</span><br><span class="line">            min.pop();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">top</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> st.peek();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">min</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> min.peek();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-40-最小的k个数"><a href="#剑指-Offer-40-最小的k个数" class="headerlink" title="剑指 Offer 40. 最小的k个数"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/zui-xiao-de-kge-shu-lcof/">剑指 Offer 40. 最小的k个数</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; ans;</span><br><span class="line">    <span class="function">vector&lt;<span class="type">int</span>&gt; <span class="title">getLeastNumbers</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; arr, <span class="type">int</span> k)</span> </span>&#123;</span><br><span class="line">        <span class="built_in">qsort</span>(arr, <span class="number">0</span>, (<span class="type">int</span>)arr.<span class="built_in">size</span>() - <span class="number">1</span>, k);</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">qsort</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; a, <span class="type">int</span> l, <span class="type">int</span> r, <span class="type">int</span> k)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(l &gt; r || k &lt;= <span class="number">0</span>) <span class="keyword">return</span>; <span class="comment">// 注意 k &lt;= 0</span></span><br><span class="line">        <span class="type">int</span> i = l - <span class="number">1</span>, j = r + <span class="number">1</span>, x = a[(l + r) / <span class="number">2</span>];</span><br><span class="line">        <span class="keyword">while</span>(i &lt; j) &#123;</span><br><span class="line">            <span class="keyword">do</span> &#123; ++i; &#125; <span class="keyword">while</span>(a[i] &lt; x);</span><br><span class="line">            <span class="keyword">do</span> &#123; --j; &#125; <span class="keyword">while</span>(a[j] &gt; x);</span><br><span class="line">            <span class="keyword">if</span>(i &lt; j) &#123;</span><br><span class="line">                <span class="built_in">swap</span>(a[i], a[j]);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(k &gt;= j - l + <span class="number">1</span>) &#123; <span class="comment">// 注意这里是 &gt;= (否则下面会递归爆栈)</span></span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> idx = l; idx &lt;= j; idx++) &#123;</span><br><span class="line">                ans.<span class="built_in">push_back</span>(a[idx]);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="built_in">qsort</span>(a, j + <span class="number">1</span>, r, k - (j - l + <span class="number">1</span>));</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="built_in">qsort</span>(a, l, j, k);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h4 id="剑指-Offer-41-数据流中的中位数"><a href="#剑指-Offer-41-数据流中的中位数" class="headerlink" title="剑指 Offer 41. 数据流中的中位数"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof/">剑指 Offer 41. 数据流中的中位数</a></h4><p>unfinished</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">MedianFinder</span> &#123;</span><br><span class="line"></span><br><span class="line">    PriorityQueue&lt;Integer&gt; pq1, pq2;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">/** initialize your data structure here. */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">MedianFinder</span><span class="params">()</span> &#123;</span><br><span class="line">        pq1 = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;&gt;((o1, o2) -&gt; o2 - o1);</span><br><span class="line">        pq2 = <span class="keyword">new</span> <span class="title class_">PriorityQueue</span>&lt;&gt;();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">addNum</span><span class="params">(<span class="type">int</span> num)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(pq1.size() == pq2.size()) &#123;</span><br><span class="line">            pq2.offer(num);</span><br><span class="line">            pq1.offer(pq2.poll());</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            pq1.offer(num);</span><br><span class="line">            pq2.offer(pq1.poll());   </span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="type">double</span> <span class="title function_">findMedian</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(pq1.size() == pq2.size()) &#123;</span><br><span class="line">            <span class="type">double</span> <span class="variable">res</span> <span class="operator">=</span> (pq1.peek() + pq2.peek()) / <span class="number">2.0</span>;</span><br><span class="line">            <span class="keyword">return</span> res;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> pq1.peek();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>







<h4 id="剑指-Offer-31-栈的压入、弹出序列"><a href="#剑指-Offer-31-栈的压入、弹出序列" class="headerlink" title="剑指 Offer 31. 栈的压入、弹出序列"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/zhan-de-ya-ru-dan-chu-xu-lie-lcof/">剑指 Offer 31. 栈的压入、弹出序列</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 模拟就好</span></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">bool</span> <span class="title">validateStackSequences</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; pushed, vector&lt;<span class="type">int</span>&gt;&amp; popped)</span> </span>&#123;</span><br><span class="line">        stack&lt;<span class="type">int</span>&gt; st;</span><br><span class="line">        <span class="type">int</span> pos = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> x : pushed) &#123;</span><br><span class="line">            st.<span class="built_in">push</span>(x);</span><br><span class="line">            <span class="comment">// 能出栈就立即出栈，否则后面进栈的元素就会影响顺序</span></span><br><span class="line">            <span class="keyword">while</span>(!st.<span class="built_in">empty</span>() &amp;&amp; st.<span class="built_in">top</span>() == popped[pos] )&#123;</span><br><span class="line">                st.<span class="built_in">pop</span>();</span><br><span class="line">                ++pos;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> st.<span class="built_in">empty</span>();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-42-连续子数组的最大和"><a href="#剑指-Offer-42-连续子数组的最大和" class="headerlink" title="剑指 Offer 42. 连续子数组的最大和"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/lian-xu-zi-shu-zu-de-zui-da-he-lcof/">剑指 Offer 42. 连续子数组的最大和</a></h4><p>前缀和</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">maxSubArray</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> ans = <span class="number">-1e9</span>, preMin = <span class="number">0</span>;</span><br><span class="line">        <span class="type">int</span> cnt = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> x : nums) &#123;</span><br><span class="line">            cnt += x;</span><br><span class="line">            ans = <span class="built_in">max</span>(ans, cnt - preMin);</span><br><span class="line">            preMin = <span class="built_in">min</span>(preMin, cnt);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-36-二叉搜索树与双向链表"><a href="#剑指-Offer-36-二叉搜索树与双向链表" class="headerlink" title="剑指 Offer 36. 二叉搜索树与双向链表"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof/">剑指 Offer 36. 二叉搜索树与双向链表</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"></span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-39-数组中出现次数超过一半的数字"><a href="#剑指-Offer-39-数组中出现次数超过一半的数字" class="headerlink" title="剑指 Offer 39. 数组中出现次数超过一半的数字"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/shu-zu-zhong-chu-xian-ci-shu-chao-guo-yi-ban-de-shu-zi-lcof/">剑指 Offer 39. 数组中出现次数超过一半的数字</a></h4><p>领导者算法</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">majorityElement</span><span class="params">(vector&lt;<span class="type">int</span>&gt;&amp; nums)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> leader, cnt = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; nums.<span class="built_in">size</span>(); i++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(cnt == <span class="number">0</span>) &#123;</span><br><span class="line">                cnt = <span class="number">1</span>;</span><br><span class="line">                leader = nums[i];</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">if</span>(nums[i] == leader) &#123;</span><br><span class="line">                    ++cnt;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    --cnt;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> leader;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>







<h4 id="剑指-Offer-37-序列化二叉树"><a href="#剑指-Offer-37-序列化二叉树" class="headerlink" title="剑指 Offer 37. 序列化二叉树"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/xu-lie-hua-er-cha-shu-lcof/">剑指 Offer 37. 序列化二叉树</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for a binary tree node.</span></span><br><span class="line"><span class="comment"> * public class TreeNode &#123;</span></span><br><span class="line"><span class="comment"> *     int val;</span></span><br><span class="line"><span class="comment"> *     TreeNode left;</span></span><br><span class="line"><span class="comment"> *     TreeNode right;</span></span><br><span class="line"><span class="comment"> *     TreeNode(int x) &#123; val = x; &#125;</span></span><br><span class="line"><span class="comment"> * &#125;</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Codec</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Encodes a tree to a single string.</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">serialize</span><span class="params">(TreeNode root)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 层次遍历这棵树，null 值使用 “ ”表示</span></span><br><span class="line">        List&lt;String&gt; list = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">        Deque&lt;TreeNode&gt; q = <span class="keyword">new</span> LinkedList&lt;&gt;();</span><br><span class="line">        q.<span class="built_in">offer</span>(root);</span><br><span class="line">        <span class="keyword">while</span>(!q.<span class="built_in">isEmpty</span>()) &#123;</span><br><span class="line">            TreeNode node = q.<span class="built_in">poll</span>();</span><br><span class="line">            <span class="keyword">if</span>(node == null) &#123;</span><br><span class="line">                list.<span class="built_in">add</span>(<span class="string">&quot; &quot;</span>);</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                list.<span class="built_in">add</span>(<span class="string">&quot;&quot;</span> + node.val);</span><br><span class="line">            &#125;</span><br><span class="line">            q.<span class="built_in">offer</span>(node.left);</span><br><span class="line">            q.<span class="built_in">offer</span>(node.right);</span><br><span class="line">        &#125;</span><br><span class="line">        StringBuilder sb = <span class="keyword">new</span> <span class="built_in">StringBuilder</span>();</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; list.<span class="built_in">size</span>(); i++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(i &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                sb.<span class="built_in">append</span>(<span class="string">&quot;,&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            sb.<span class="built_in">append</span>(list.<span class="built_in">get</span>(i));</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sb.<span class="built_in">toString</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Decodes your encoded data to tree.</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> TreeNode <span class="title">deserialize</span><span class="params">(String data)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> pos = <span class="number">0</span>;</span><br><span class="line">        String[] split = data.<span class="built_in">split</span>(<span class="string">&quot;,&quot;</span>);</span><br><span class="line">        <span class="keyword">if</span>(split.length == <span class="number">0</span> || <span class="string">&quot; &quot;</span>.<span class="built_in">equals</span>(split[<span class="number">0</span>])) <span class="keyword">return</span> null;</span><br><span class="line">        TreeNode root = <span class="keyword">new</span> <span class="built_in">TreeNode</span>(Integer.<span class="built_in">parseInt</span>(split[pos++]));</span><br><span class="line">        <span class="comment">// 如何重建这棵树？</span></span><br><span class="line">        Deque&lt;TreeNode&gt; q = <span class="keyword">new</span> ArrayDeque&lt;&gt;();</span><br><span class="line">        q.<span class="built_in">offer</span>(root);</span><br><span class="line">        <span class="keyword">while</span>(q.<span class="built_in">size</span>() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="type">int</span> size = q.<span class="built_in">size</span>();</span><br><span class="line">            <span class="type">int</span> len = size;</span><br><span class="line">            List&lt;TreeNode&gt; list = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">            List&lt;String&gt; valList = <span class="keyword">new</span> ArrayList&lt;&gt;();</span><br><span class="line">            <span class="comment">// 一次性取出这一层所有的节点，以及用于构建左右子树的字符串</span></span><br><span class="line">            <span class="keyword">while</span>(size-- &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                list.<span class="built_in">add</span>(q.<span class="built_in">poll</span>());</span><br><span class="line">                valList.<span class="built_in">add</span>(split[pos++]);</span><br><span class="line">                valList.<span class="built_in">add</span>(split[pos++]);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; len; i++) &#123;</span><br><span class="line">                TreeNode node = list.<span class="built_in">get</span>(i);</span><br><span class="line">                String leftS = valList.<span class="built_in">get</span>(i * <span class="number">2</span>);</span><br><span class="line">                String rightS = valList.<span class="built_in">get</span>(i * <span class="number">2</span> + <span class="number">1</span>);</span><br><span class="line">                node.left = node.right = null;</span><br><span class="line">                <span class="keyword">if</span>(!<span class="string">&quot; &quot;</span>.<span class="built_in">equals</span>(leftS)) &#123;</span><br><span class="line">                    node.left = <span class="keyword">new</span> <span class="built_in">TreeNode</span>(Integer.<span class="built_in">parseInt</span>(leftS));</span><br><span class="line">                    q.<span class="built_in">offer</span>(node.left);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span>(!<span class="string">&quot; &quot;</span>.<span class="built_in">equals</span>(rightS)) &#123;</span><br><span class="line">                    node.right = <span class="keyword">new</span> <span class="built_in">TreeNode</span>(Integer.<span class="built_in">parseInt</span>(rightS));</span><br><span class="line">                    q.<span class="built_in">offer</span>(node.right);</span><br><span class="line">                &#125;              </span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> root;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-36-二叉搜索树与双向链表-1"><a href="#剑指-Offer-36-二叉搜索树与双向链表-1" class="headerlink" title="剑指 Offer 36. 二叉搜索树与双向链表"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/er-cha-sou-suo-shu-yu-shuang-xiang-lian-biao-lcof/">剑指 Offer 36. 二叉搜索树与双向链表</a></h4><p>unfinished</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    Node *first = <span class="literal">nullptr</span>, *last = <span class="literal">nullptr</span>; </span><br><span class="line">    <span class="function">Node* <span class="title">treeToDoublyList</span><span class="params">(Node* root)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(root == <span class="literal">nullptr</span>) <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">        <span class="built_in">help</span>(root);</span><br><span class="line">        <span class="comment">// 循环链表的要求</span></span><br><span class="line">        first-&gt;left = last;</span><br><span class="line">        last-&gt;right = first;  </span><br><span class="line">        <span class="keyword">return</span> first;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">help</span><span class="params">(Node* root)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(!root) <span class="keyword">return</span>;</span><br><span class="line">        <span class="built_in">help</span>(root-&gt;left);</span><br><span class="line">        <span class="keyword">if</span>(last) &#123;</span><br><span class="line">            last-&gt;right = root;</span><br><span class="line">            root-&gt;left = last;</span><br><span class="line">            last = root;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            first = last = root;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">help</span>(root-&gt;right);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>



<h4 id="剑指-Offer-38-字符串的排列"><a href="#剑指-Offer-38-字符串的排列" class="headerlink" title="剑指 Offer 38. 字符串的排列"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/zi-fu-chuan-de-pai-lie-lcof/">剑指 Offer 38. 字符串的排列</a></h4><p>关键是如何去重（当然不能使用set）</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    vector&lt;string&gt; ans;</span><br><span class="line">    <span class="type">int</span> len;</span><br><span class="line">    string s,temp;</span><br><span class="line">    vector&lt;<span class="type">bool</span>&gt; vis;</span><br><span class="line">    <span class="function">vector&lt;string&gt; <span class="title">permutation</span><span class="params">(string s)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 将字符排序（用于去重）</span></span><br><span class="line">        <span class="built_in">sort</span>(s.<span class="built_in">begin</span>(),s.<span class="built_in">end</span>());</span><br><span class="line">        <span class="keyword">this</span>-&gt;s = s;</span><br><span class="line">        len = s.<span class="built_in">size</span>();</span><br><span class="line">        vis = <span class="built_in">vector</span>&lt;<span class="type">bool</span>&gt;(len,<span class="literal">false</span>);</span><br><span class="line">        <span class="built_in">dfs</span>(<span class="number">0</span>);</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="type">void</span> <span class="title">dfs</span><span class="params">(<span class="type">int</span> pos)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(pos == len) &#123;</span><br><span class="line">            ans.<span class="built_in">push_back</span>(temp);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; len; i++) &#123;</span><br><span class="line">            <span class="keyword">if</span>(!vis[i]) &#123;</span><br><span class="line">                <span class="comment">// 判断条件注意一下</span></span><br><span class="line">                <span class="comment">// 比如有两个相同字符 c，这两个可以接连选，当然也可以接连不选（也就是11 、00都是可以接受的）</span></span><br><span class="line">                <span class="comment">// 但是 10、01只能要一个，我们选择 10</span></span><br><span class="line">                <span class="keyword">if</span>(i &gt; <span class="number">0</span> &amp;&amp; s[i] == s[i<span class="number">-1</span>] &amp;&amp; !vis[i<span class="number">-1</span>]) <span class="keyword">continue</span>; <span class="comment">// key point</span></span><br><span class="line">                temp.<span class="built_in">push_back</span>(s[i]);</span><br><span class="line">                vis[i] = <span class="number">1</span>;</span><br><span class="line">                <span class="built_in">dfs</span>(pos+<span class="number">1</span>);</span><br><span class="line">                temp.<span class="built_in">pop_back</span>();</span><br><span class="line">                vis[i] = <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>





<h4 id="剑指-Offer-43-1～n-整数中-1-出现的次数"><a href="#剑指-Offer-43-1～n-整数中-1-出现的次数" class="headerlink" title="剑指 Offer 43. 1～n 整数中 1 出现的次数"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/1nzheng-shu-zhong-1chu-xian-de-ci-shu-lcof/">剑指 Offer 43. 1～n 整数中 1 出现的次数</a></h4><figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="function"><span class="type">int</span> <span class="title">countDigitOne</span><span class="params">(<span class="type">int</span> n)</span> </span>&#123;</span><br><span class="line">        <span class="type">int</span> ans = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">long</span> <span class="type">long</span> mul = <span class="number">1</span>; n &gt;= mul; mul *= <span class="number">10</span>) &#123;</span><br><span class="line">            ans += (n/(mul * <span class="number">10</span>)) * mul;</span><br><span class="line">            ans += <span class="built_in">min</span>(<span class="built_in">max</span>( n % (mul * <span class="number">10</span>) - mul + <span class="number">1</span>, <span class="number">0LL</span>), mul); <span class="comment">// 0LL 表示 long long 的整形量</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> ans;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>







<h4 id="剑指-Offer-35-复杂链表的复制"><a href="#剑指-Offer-35-复杂链表的复制" class="headerlink" title="剑指 Offer 35. 复杂链表的复制"></a><a target="_blank" rel="noopener" href="https://leetcode-cn.com/problems/fu-za-lian-biao-de-fu-zhi-lcof/">剑指 Offer 35. 复杂链表的复制</a></h4><p>unfinished</p>
<figure class="highlight cpp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Solution</span> &#123;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    unordered_map&lt;Node*, Node*&gt; cachedNode;</span><br><span class="line"></span><br><span class="line">    <span class="function">Node* <span class="title">copyRandomList</span><span class="params">(Node* head)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (!cachedNode.<span class="built_in">count</span>(head)) &#123;</span><br><span class="line">            Node* headNew = <span class="keyword">new</span> <span class="built_in">Node</span>(head-&gt;val);</span><br><span class="line">            cachedNode[head] = headNew;</span><br><span class="line">            headNew-&gt;next = <span class="built_in">copyRandomList</span>(head-&gt;next);</span><br><span class="line">            headNew-&gt;random = <span class="built_in">copyRandomList</span>(head-&gt;random);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> cachedNode[head];</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br></pre></td></tr></table></figure>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/03/TCP-2/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/03/TCP-2/" class="post-title-link" itemprop="url">TCP —— 重传机制、流量控制、拥塞控制</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-03 08:15:09" itemprop="dateCreated datePublished" datetime="2021-12-03T08:15:09+08:00">2021-12-03</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-12-13 16:18:56" itemprop="dateModified" datetime="2021-12-13T16:18:56+08:00">2021-12-13</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/" itemprop="url" rel="index"><span itemprop="name">计算机网络</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h2 id="TCP-是如何保证可靠性的"><a href="#TCP-是如何保证可靠性的" class="headerlink" title="TCP 是如何保证可靠性的"></a>TCP 是如何保证可靠性的</h2><ul>
<li>数据分块：应用数据被分割成 TCP 认为最适合发送的数据块。</li>
<li>序列号和确认应答：TCP 给发送的每一个包进行编号，在传输的过程中，每次接收方收到数据后，都会对传输方进行确认应答，即发送 ACK 报文，这个 ACK 报文当中带有对应的确认序列号，告诉发送方成功接收了哪些数据以及下一次的数据从哪里开始发。除此之外，接收方可以根据序列号对数据包进行排序，把有序数据传送给应用层，并丢弃重复的数据。</li>
<li>校验和： TCP 将保持它首部和数据部分的检验和。这是一个端到端的检验和，目的是检测数据在传输过程中的任何变化。如果收到报文段的检验和有差错，TCP 将丢弃这个报文段并且不确认收到此报文段。</li>
<li>流量控制： TCP 连接的双方都有一个固定大小的缓冲空间，发送方发送的数据量不能超过接收端缓冲区的大小。当接收方来不及处理发送方的数据，会提示发送方降低发送的速率，防止产生丢包。TCP 通过滑动窗口协议来支持流量控制机制。</li>
<li>拥塞控制： 当网络某个节点发生拥塞时，减少数据的发送。</li>
<li>ARQ协议： 也是为了实现可靠传输的，它的基本原理就是每发完一个分组就停止发送，等待对方确认。在收到确认后再发下一个分组。</li>
<li>超时重传： 当 TCP 发出一个报文段后，它启动一个定时器，等待目的端确认收到这个报文段。如果超过某个时间还没有收到确认，将重发这个报文段。</li>
</ul>
<h2 id="重传机制"><a href="#重传机制" class="headerlink" title="重传机制"></a>重传机制</h2><p>TCP 实现可靠传输的⽅式之⼀，是通过序列号与确认应答。</p>
<p>在 TCP 中，当发送端的数据到达接收主机时，接收端主机会返回⼀个确认应答消息，表示已收到消息</p>
<p>所以 TCP 针对数据包丢失的情况，会⽤重传机制解决。</p>
<p>接下来说说常⻅的重传机制：</p>
<ul>
<li><p>超时重传</p>
</li>
<li><p>快速重传</p>
</li>
<li><p>SACK</p>
</li>
<li><p>D-SACK</p>
</li>
</ul>
<h3 id="超时重传"><a href="#超时重传" class="headerlink" title="超时重传"></a>超时重传</h3><p>重传机制的其中⼀个⽅式，就是在发送数据时，设定⼀个定时器，当超过指定的时间后，没有收到对⽅的 ACK</p>
<p>确认应答报⽂，就会重发该数据，也就是我们常说的超时重传。</p>
<p>TCP 会在以下两种情况发⽣超时重传：</p>
<ol>
<li><p>数据包丢失</p>
</li>
<li><p>确认应答丢失</p>
</li>
</ol>
<img src="rtt.png" style="zoom:60%;" />





<p>RTT （Round-Trip Time 往返时延）</p>
<p>RTT 就是数据从⽹络⼀端传送到另⼀端所需的时间，也就是包的往返时间。</p>
<p>超时重传时间是以 RTO （Retransmission Timeout 超时重传时间）表示。</p>
<p>当超时时间 <strong>RTO</strong> 较⼤时，重发就慢，丢了⽼半天才重发，没有效率，性能差；</p>
<p>当超时时间 <strong>RTO</strong> 较⼩时，会导致可能并没有丢就重发，于是重发的就快，会增加⽹络拥塞，导致更多的超</p>
<p>时，更多的超时导致更多的重发。</p>
<p>所以<strong>精确的测量超时时间 RTO 的值是⾮常重要的</strong>，这可让我们的重传机制更⾼效。</p>
<p>根据上述的两种情况，我们可以得知，<strong>超时重传时间RTO的值应该略⼤于报⽂往返RTT的值</strong>。</p>
<p>实际上「报⽂往返 RTT 的值」是经常变化的，因为我们的⽹络也是时常变化的。也就因为「报⽂往返 RTT 的值」</p>
<p>是经常波动变化的，所以「超时重传时间 RTO 的值」应该是⼀个动态变化的值。</p>
<p>我们来看看 Linux 是如何计算 RTO 的呢？</p>
<p>估计往返时间，通常需要采样以下两个：</p>
<ul>
<li>需要 TCP 通过<strong>采样 RTT 的时间，然后进⾏加权平均，算出⼀个平滑 RTT 的值</strong>，⽽且这个值还是要不断变化</li>
</ul>
<p>的，因为⽹络状况不断地变化。</p>
<ul>
<li>除了采样 RTT，还要采样 <strong>RTT 的波动范围</strong>，这样就避免如果 RTT 有⼀个⼤的波动的话，很难被发现的情况。</li>
</ul>
<p>RFC6289 建议使⽤以下的公式计算 RTO：</p>
<img src="rto.png" style="zoom:50%;" />



<p>如果超时重发的数据，<strong>再次超时的时候，⼜需要重传的时候，TCP 的策略是超时间隔加倍</strong>。</p>
<p>也就是每当遇到⼀次超时重传的时候，都会将下⼀次超时时间间隔设为先前值的两倍。两次超时，就说明⽹络环境差，不宜频繁反复发送。</p>
<h3 id="快速重传"><a href="#快速重传" class="headerlink" title="快速重传"></a>快速重传</h3><p>TCP 还有另外⼀种快速重传（<strong>Fast Retransmit</strong>）机制，它<strong>不以时间为驱动，⽽是以数据驱动重传</strong>。</p>
<img src="fast-retransmission.png" style="zoom:80%;" />



<blockquote>
<p>在上图，发送⽅发出了 1，2，3，4，5 份数据：</p>
<ol>
<li>第⼀份 Seq1 先送到了，于是就 Ack 回 2；</li>
<li>结果 Seq2 因为某些原因没收到，Seq3 到达了，于是还是 Ack 回 2；</li>
<li>后⾯的 Seq4 和 Seq5 都到了，但还是 Ack 回 2，因为 Seq2 还是没有收到；</li>
<li>发送端收到了三个 <strong>Ack &#x3D; 2</strong> 的确认，知道了 <strong>Seq2</strong> 还没有收到，就会在定时器过期之前，重传丢失的 <strong>Seq2</strong>。</li>
<li>最后，收到了 Seq2，此时因为 Seq3，Seq4，Seq5 都收到了，于是 Ack 回 6 。</li>
</ol>
</blockquote>
<p>快速重传的⼯作⽅式是<strong>当收到三个相同的 ACK 报⽂时，会在定时器过期之前，重传丢失的报⽂段</strong>。</p>
<h3 id="SACK"><a href="#SACK" class="headerlink" title="SACK"></a>SACK</h3><p>为了解决不知道该重传哪些 TCP 报⽂，于是就有 SACK ⽅法。 <strong>SACK （ Selective Acknowledgment 选择性确认）</strong>。</p>
<p>这种⽅式需要在 TCP 头部「选项」字段⾥加⼀个 SACK 的字段，它可以<strong>将缓存的区间发送给发送⽅，这样发送</strong></p>
<p><strong>⽅就可以知道哪些数据收到了，哪些数据没收到</strong>，知道了这些信息，就可以只重传丢失的数据。</p>
<p><img src="/2021/12/03/TCP-2/sack.png"></p>
<p>如上图，发送⽅收到了三次同样的 ACK 确认报⽂，于是就会触发快速重发机制，通过 SACK 信息发现只有</p>
<p>200~299 这段数据丢失，则重发时，就只选择了这个 TCP 段进⾏重复。</p>
<h3 id="Duplicate-SACK"><a href="#Duplicate-SACK" class="headerlink" title="Duplicate SACK"></a>Duplicate SACK</h3><p>使⽤了 SACK 来告诉「发送⽅」有哪些数据被重复接收了。</p>
<p>具体的，就是当 ACK大于sack选项的区间时，表明收到了重复的包。</p>
<p>可⻅， D-SACK 有这么⼏个好处：</p>
<ol>
<li><p>可以让「发送⽅」知道，是发出去的包丢了，还是接收⽅回应的 ACK 包丢了;</p>
</li>
<li><p>可以知道是不是「发送⽅」的数据包被⽹络延迟了;</p>
</li>
<li><p>可以知道⽹络中是不是把「发送⽅」的数据包给复制了;</p>
</li>
</ol>
<h2 id="滑动窗口"><a href="#滑动窗口" class="headerlink" title="滑动窗口"></a>滑动窗口</h2><p>停等式发送与接收的方式的缺点是：数据包的往返时间越⻓，通信的效率就越低。</p>
<p>为解决这个问题，TCP 引⼊了窗⼝这个概念。即使在往返时间较⻓的情况下，它也不会降低⽹络通信的效率。</p>
<p>那么有了窗⼝，就可以指定<strong>窗⼝⼤⼩</strong>，窗⼝⼤⼩就是指<strong>⽆需等待确认应答，⽽可以继续发送数据的最⼤值</strong>。</p>
<blockquote>
<p><strong>UDP 为什么是不可靠的？</strong><br>UDP 只有一个 socket 接收缓冲区，没有 socket 发送缓冲区，即只要有数据就发，不管对方是否可以正确接收。而在<strong>对方的 socket 接收缓冲区满了之后，新来的数据报无法进入到 socket 接受缓冲区，此数据报就会被丢弃</strong>，因此 UDP 不能保证数据能够到达目的地，此外，<strong>UDP 也没有流量控制和重传机制</strong>，故UDP的数据传输是不可靠的。</p>
</blockquote>
<h3 id="操作系统缓冲区与滑动窗⼝的关系"><a href="#操作系统缓冲区与滑动窗⼝的关系" class="headerlink" title="操作系统缓冲区与滑动窗⼝的关系"></a>操作系统缓冲区与滑动窗⼝的关系</h3><p><strong>窗⼝的实现实际上是操作系统开辟的⼀个缓存空间</strong>，<strong>发送⽅主机在等到确认应答返回之前，必须在缓冲区中保留已发送的数据</strong>。<strong>如果按期收到确认应答，此时数据就可以从缓存区清除</strong>。</p>
<p>前面我们假定了发送窗⼝和接收窗⼝是不变的，但是实际上，发送窗⼝和接收窗⼝中所存放的字节数，都是放在操作系统内存缓冲区中的，⽽<strong>操作系统的缓冲区，会被操作系统调整</strong>。</p>
<p><strong>当应⽤进程没办法及时读取缓冲区的内容时，也会对我们的缓冲区造成影响</strong>。</p>
<img src="sum-ack.png" style="zoom:80%;" />



<p>图中的 ACK 600 确认应答报⽂丢失，也没关系，因为可以通过下⼀个确认应答进⾏确认，只要发送⽅收到了 ACK</p>
<p>700 确认应答，就意味着 700 之前的所有数据「接收⽅」都收到了。这个模式就叫<strong>累计确认</strong>或者<strong>累计应答</strong>。</p>
<h3 id="窗口大小的协商"><a href="#窗口大小的协商" class="headerlink" title="窗口大小的协商"></a>窗口大小的协商</h3><p>TCP 头⾥有⼀个字段叫 Window ，也就是窗⼝⼤⼩。</p>
<p>这个字段是<strong>接收端告诉发送端⾃⼰还有多少缓冲区可以接收数据</strong>。于是发送端就可以根据这个接收端的处理能⼒来</p>
<p>发送数据，⽽不会导致接收端处理不过来。</p>
<p>所以，<strong>通常窗⼝的⼤⼩是由接收⽅的窗⼝⼤⼩来决定的</strong>。<strong>发送⽅发送的数据⼤⼩不能超过接收⽅的窗⼝⼤⼩，否则接收⽅就⽆法正常接收到数据。</strong>（虽然超过对端的窗口大小导致没有收到ACK，最终也会触发超时重传的，但是会导致网络的拥塞）。</p>
<p><strong>接收窗⼝和发送窗⼝的⼤⼩是相等的吗？</strong></p>
<p>并不是完全相等，接收窗⼝的⼤⼩是约等于发送窗⼝的⼤⼩的。</p>
<p>因为滑动窗⼝并不是⼀成不变的。⽐如，当接收⽅的应⽤进程读取数据的速度⾮常快的话，这样的话接收窗⼝可以</p>
<p>很快的就空缺出来。那么新的接收窗⼝⼤⼩，是通过 TCP 报⽂中的 Windows 字段来告诉发送⽅。那么这个传输过程是存在时延的，所以接收窗⼝和发送窗⼝是约等于的关系。</p>
<h3 id="发送⽅的滑动窗⼝"><a href="#发送⽅的滑动窗⼝" class="headerlink" title="发送⽅的滑动窗⼝"></a>发送⽅的滑动窗⼝</h3><p>我们先来看看发送⽅的窗⼝，下图就是发送⽅缓存的数据，根据处理的情况分成四个部分，其中深蓝⾊⽅框是发送</p>
<p>窗⼝，紫⾊⽅框是可⽤窗⼝：</p>
<p><img src="/2021/12/03/TCP-2/window1.png"></p>
<ul>
<li>#1 是<strong>已发送并收到 ACK确认</strong>的数据：1~31 字节</li>
<li>#2 是<strong>已发送但未收到 ACK确认</strong>的数据：32~45 字节</li>
<li>#3 是<strong>未发送但总⼤⼩在接收⽅处理范围内</strong>（接收⽅还有空间）：46~51字节</li>
<li>#4 是<strong>未发送但总⼤⼩超过接收⽅处理范围</strong>（接收⽅没有空间）：52字节以后</li>
</ul>
<h3 id="接收⽅的滑动窗⼝"><a href="#接收⽅的滑动窗⼝" class="headerlink" title="接收⽅的滑动窗⼝"></a>接收⽅的滑动窗⼝</h3><p>接下来我们看看接收⽅的窗⼝，接收窗⼝相对简单⼀些，根据处理的情况划分成三个部分：</p>
<p><img src="/2021/12/03/TCP-2/window2.png"></p>
<ul>
<li>#1 + #2 是<strong>已成功接收并确认的数据</strong>（等待应⽤进程读取）；</li>
<li>#3 是<strong>未收到数据但可以接收的数据；</strong></li>
<li>#4 是<strong>未收到数据并不可以接收的数据</strong>；</li>
</ul>
<h2 id="流量控制"><a href="#流量控制" class="headerlink" title="流量控制"></a>流量控制</h2><p>发送⽅不能没有限制地发数据给接收⽅，要考虑接收⽅处理能⼒。</p>
<p>如果⼀直⽆脑的发数据给对⽅，但对⽅处理不过来，那么就会导致触发重发机制，从⽽导致⽹络流量的⽆端的浪费。</p>
<p>为了解决这种现象发⽣，<strong>TCP</strong> 提供⼀种机制可以<strong>让「发送⽅」根据「接收⽅」的实际接收能⼒控制发送的数据量</strong>，这就是所谓的<strong>流量控制</strong>。</p>
<p>使用滑动窗口来做到流量控制，这部内容在上面已经得到很好的描述了。</p>
<h3 id="窗⼝关闭"><a href="#窗⼝关闭" class="headerlink" title="窗⼝关闭"></a>窗⼝关闭</h3><p>在前⾯我们都看到了，TCP 通过让接收⽅指明希望从发送⽅接收的数据⼤⼩（窗⼝⼤⼩）来进⾏流量控制。<br>如果窗⼝⼤⼩为 0 时，就会阻⽌发送⽅给接收⽅传递数据，直到窗⼝变为⾮ 0 为⽌，这就是窗⼝关闭。</p>
<p><strong>窗⼝关闭潜在的危险</strong></p>
<p><strong>接收⽅向发送⽅通告窗⼝⼤⼩时，是通过 ACK 报⽂来通告的</strong>。</p>
<p>那么，当发⽣窗⼝关闭时，接收⽅处理完数据后，会向发送⽅通告⼀个窗⼝⾮ 0 的 ACK 报⽂，如果这个通告窗⼝的 ACK 报⽂在⽹络中丢失了，那麻烦就⼤了。</p>
<img src="window-closed.png" style="zoom:67%;" />



<p>这会导致<strong>发送⽅⼀直等待接收⽅的⾮ 0 窗⼝通知</strong>，接收⽅也⼀直等待发送⽅的数据，如不采取措施，这种相互等待的过程，会造成了死锁的现象。</p>
<p>为了解决这个问题，TCP 为每个连接设有⼀个持续定时器，只要 <strong>TCP</strong> 连接⼀⽅收到对⽅的零窗⼝通知，就启动持续计时器。</p>
<p>如果持续计时器超时，就会发送窗⼝探测 <strong>( Window probe )</strong> 报⽂，⽽对⽅在确认这个探测报⽂时，给出⾃⼰现在的接收窗⼝⼤⼩。</p>
<p><img src="/2021/12/03/TCP-2/window-probe.png"></p>
<p>窗⼝探测的次数⼀般为 3 次，每次⼤约 30-60 秒（不同的实现可能会不⼀样）。如果 3 次过后接收窗⼝还是 0 的话，<strong>有的 TCP 实现就会发 RST 报⽂来中断连接</strong>。</p>
<h3 id="低能窗口综合征"><a href="#低能窗口综合征" class="headerlink" title="低能窗口综合征"></a>低能窗口综合征</h3><p>如果接收⽅太忙了，来不及取⾛接收窗⼝⾥的数据，那么就会导致发送⽅的发送窗⼝越来越⼩。</p>
<p>到最后，<strong>如果接收⽅腾出⼏个字节并告诉发送⽅现在有⼏个字节的窗⼝，⽽发送⽅会义⽆反顾地发送这⼏个字节</strong>，</p>
<p>这就是低能窗⼝综合症。</p>
<p>要知道，我们的 TCP + IP 头有 40 个字节，为了传输那⼏个字节的数据，要达上这么⼤的开销，这太不经济</p>
<p>了。</p>
<p>所以，低能窗⼝综合症的现象是可以发⽣在发送⽅和接收⽅：</p>
<ol>
<li><p>接收⽅可以通告⼀个⼩的窗⼝；</p>
</li>
<li><p>发送⽅可以发送⼩数据。</p>
</li>
</ol>
<p>当然解决问题的思路也从这两方面入手：</p>
<ol>
<li><strong>接收⽅不通告⼩窗⼝</strong></li>
<li><strong>发送⽅避免发送⼩数据</strong></li>
</ol>
<p><strong>接收⽅通常的策略如下</strong>:</p>
<p>当「窗⼝⼤⼩」⼩于 min( MSS，缓存空间&#x2F;2 ) ，也就是<strong>当窗口大小⼩于 MSS 与 1&#x2F;2 缓存⼤⼩中的最⼩值时，就会向发送⽅通告窗⼝为 0</strong> ，也就阻⽌了发送⽅再发数据过来。</p>
<h4 id="Nagle算法"><a href="#Nagle算法" class="headerlink" title="Nagle算法"></a>Nagle算法</h4><p><strong>发送⽅通常的策略</strong></p>
<p>使⽤ Nagle 算法，该算法的思路是<strong>延时处理</strong>，它满⾜以下两个条件中的⼀条才可以发送数据：</p>
<ul>
<li><p><strong>要等到窗⼝⼤⼩ &gt;&#x3D; MSS 或是数据⼤⼩ &gt;&#x3D; MSS</strong> ;</p>
</li>
<li><p><strong>收到之前发送数据的 ack 回包</strong> ;</p>
</li>
</ul>
<p>只要没满⾜上⾯条件中的⼀条，发送⽅⼀直在囤积数据，直到满⾜上⾯的发送条件。</p>
<p>另外，<strong>Nagle 算法默认是打开的</strong>，如果对于⼀些需要⼩数据包交互的场景的程序，⽐如，telnet 或 ssh 这样的交互性⽐较强的程序，则需要关闭 Nagle 算法。</p>
<p>可以在 Socket 设置 <code>TCP_NODELAY</code> 选项来关闭这个算法（关闭 Nagle 算法没有全局参数，需要根据每个应⽤</p>
<p>⾃⼰的特点来关闭）</p>
<p>另外，<strong>Nagle 算法默认是打开的</strong>，如果对于⼀些需要⼩数据包交互的场景的程序，⽐如，telnet 或 ssh 这样的交互性⽐较强的程序，则需要关闭 Nagle 算法。</p>
<p>可以在 Socket 设置 <code>TCP_NODELAY</code> 选项来关闭这个算法（关闭 Nagle 算法没有全局参数，需要根据每个应⽤⾃⼰的特点来关闭）</p>
<h2 id="拥塞控制"><a href="#拥塞控制" class="headerlink" title="拥塞控制"></a>拥塞控制</h2><blockquote>
<p>拥塞控制是解决网络带宽资源有限的问题，流量控制则是解决主机缓冲区有限的问题。</p>
</blockquote>
<p>在⽹络出现拥堵时，如果继续发送⼤量数据包，可能会导致数据包时延、丢失等，这时 <strong>TCP</strong> 就会重传数据，但是</p>
<p>⼀重传就会导致⽹络的负担更重，于是会导致更⼤的延迟以及更多的丢包，这个情况就会进⼊恶性循环被不断地放</p>
<p>⼤。</p>
<p>所以，TCP 不能忽略⽹络上发⽣的事，它被设计成⼀个⽆私的协议，当⽹络发送拥塞时，TCP 会⾃我牺牲，降低</p>
<p>发送的数据量。</p>
<p>于是，就有了拥塞控制，控制的⽬的就是避免「发送⽅」的数据填满整个⽹络。</p>
<h3 id="拥塞窗口"><a href="#拥塞窗口" class="headerlink" title="拥塞窗口"></a>拥塞窗口</h3><p><strong>拥塞窗⼝ <code>cwnd</code>是发送⽅维护的⼀个的状态变量</strong>，它<strong>会根据⽹络的拥塞程度动态变化</strong>的。</p>
<p>我们在前⾯提到过发送窗⼝ swnd 和接收窗⼝ rwnd 是约等于的关系，那么由于加⼊了拥塞窗⼝的概念后，此时发送窗⼝的值是**swnd &#x3D; min(cwnd, rwnd)**，也就是拥塞窗⼝和接收窗⼝中的最⼩值。<br>拥塞窗⼝ cwnd 变化的规则：</p>
<ul>
<li>只要⽹络中没有出现拥塞， cwnd 就会增⼤；</li>
<li>但⽹络中出现了拥塞， cwnd 就减少；</li>
</ul>
<p><strong>那么怎么知道当前⽹络是否出现了拥塞呢？</strong></p>
<p><strong>只要「发送⽅」没有在规定时间内接收到 ACK 应答报⽂，也就是发⽣了超时重传</strong>，就会认为⽹络出现了⽤拥</p>
<p>塞。这是拥塞发生的一条很重要的前提。</p>
<h3 id="慢启动"><a href="#慢启动" class="headerlink" title="慢启动"></a>慢启动</h3><p>当发送方开始发送数据时，由于一开始不知道网络负荷情况，如果立即将大量的数据字节传输到网络中，那么就有可能引起网络拥塞。一个较好的方法是在一开始发送少量的数据先探测一下网络状况，即由小到大的增大发送窗口（拥塞窗口 cwnd）。慢开始的慢指的是初始时令 cwnd为 1，即一开始发送一个报文段。如果收到确认，则 cwnd &#x3D; 2，之后每收到一个确认报文，就令 cwnd &#x3D; cwnd* 2。</p>
<p>但是，为了防止拥塞窗口增长过大而引起网络拥塞，另外设置了一个<strong>慢开始门限</strong> ssthresh。⼀般来说 ssthresh 的⼤⼩是 65535 字节。</p>
<p>① <strong>当 cwnd &lt; ssthresh 时，使用上述的慢开始算法</strong>；</p>
<p>② <strong>当 cwnd &gt; ssthresh 时，停止使用慢开始，转而使用拥塞避免算法</strong>；</p>
<p>③ 当 cwnd &#x3D;&#x3D; ssthresh 时，两者均可。</p>
<p><strong>慢启动算法</strong>，发包的个数是<strong>指数性的增⻓</strong>。</p>
<h3 id="拥塞避免"><a href="#拥塞避免" class="headerlink" title="拥塞避免"></a>拥塞避免</h3><p><strong>拥塞控制是为了让拥塞窗口 cwnd 缓慢地增大</strong>，即每经过一个往返时间 RTT （往返时间定义为发送方发送数据到收到确认报文所经历的时间）就把发送方的 cwnd 值加 1，<strong>通过让 cwnd 线性增长，防止很快就遇到网络拥塞状态</strong>。</p>
<p><strong>当网络拥塞发生时</strong>，<strong>让新的慢开始门限值变为发生拥塞时候的值的一半, 并将拥塞窗口置为 1</strong> , 然后再次重复两种算法（慢开始和拥塞避免）,这时一瞬间会将网络中的数据量大量降低。</p>
<h3 id="拥塞发生"><a href="#拥塞发生" class="headerlink" title="拥塞发生"></a>拥塞发生</h3><p>当⽹络出现拥塞，也就是会发⽣数据包重传，重传机制主要有两种：</p>
<ol>
<li><p>超时重传</p>
</li>
<li><p>快速重传</p>
</li>
</ol>
<h4 id="超时重传-1"><a href="#超时重传-1" class="headerlink" title="超时重传"></a>超时重传</h4><p>一旦发生超时重传，可以说是一夜回到解放前，这个时候，</p>
<ul>
<li>cwnd &#x3D; 1</li>
<li>ssthresh &#x3D; ssthresh &#x2F; 2 ;</li>
</ul>
<p>重新开始上述过程。</p>
<h4 id="快速重传-1"><a href="#快速重传-1" class="headerlink" title="快速重传"></a>快速重传</h4><p><strong>快重传算法要求接收方每收到一个失序的报文就立即发送重复确认</strong>，<strong>不必等待超时再重传</strong>。</p>
<p>TCP 认为这种情况不严重，因为⼤部分没丢，只丢了⼀⼩部分，则 ssthresh 和 cwnd 变化如下：</p>
<ul>
<li><p>cwnd &#x3D; cwnd&#x2F;2 ，也就是设置为原来的⼀半;</p>
</li>
<li><p>ssthresh &#x3D; cwnd ;</p>
</li>
<li><p>进⼊快速恢复算法</p>
</li>
</ul>
<h3 id="快速恢复"><a href="#快速恢复" class="headerlink" title="快速恢复"></a>快速恢复</h3><p><strong>快恢复算法是和快重传算法配合使用</strong>的，该算法主要有以下两个要点：</p>
<p>① <strong>当发送方连续收到三个重复确认</strong>，执行乘法减小，慢开始门限 ssthresh 值减半；</p>
<p>② 由于发送方可能认为网络现在没有拥塞，因此与慢开始不同，<strong>把 cwnd 值设置为 ssthresh 减半之后的值，然后执行拥塞避免算法，线性增大 cwnd</strong>。</p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p><img src="/2021/12/03/TCP-2/%E6%8B%A5%E5%A1%9E%E9%81%BF%E5%85%8D.png"></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/01/Buffer-Cache-%E5%92%8C-Page-Cache/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/01/Buffer-Cache-%E5%92%8C-Page-Cache/" class="post-title-link" itemprop="url">Buffer Cache 和 Page Cache</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-01 10:57:20" itemprop="dateCreated datePublished" datetime="2021-12-01T10:57:20+08:00">2021-12-01</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          
      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/01/Redis-%E6%8C%81%E4%B9%85%E5%8C%96%E6%9C%BA%E5%88%B6/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/01/Redis-%E6%8C%81%E4%B9%85%E5%8C%96%E6%9C%BA%E5%88%B6/" class="post-title-link" itemprop="url">Redis 持久化机制</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-12-01 10:18:32" itemprop="dateCreated datePublished" datetime="2021-12-01T10:18:32+08:00">2021-12-01</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2021-12-09 14:17:59" itemprop="dateModified" datetime="2021-12-09T14:17:59+08:00">2021-12-09</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Redis/" itemprop="url" rel="index"><span itemprop="name">Redis</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h1><p>Redis是一个内存数据库，所有的数据将保存在内存中，这与传统的MySQL、Oracle、SqlServer等关系型数据库直接把数据保存到硬盘相比，Redis的读写效率非常高。但是保存在内存中也有一个很大的缺陷，一旦断电或者宕机，内存数据库中的内容将会全部丢失。为了弥补这一缺陷，Redis提供了把内存数据持久化到硬盘文件，以及通过备份文件来恢复数据的功能，即Redis持久化机制。</p>
<p>Redis支持两种方式的持久化：RDB快照和AOF。</p>
<img src="summary.jpeg" alt="img" style="zoom:60%;" />



<h1 id="RDB"><a href="#RDB" class="headerlink" title="RDB"></a>RDB</h1><p>RDB快照用官方的话来说：RDB持久化方案是按照指定时间间隔对你的数据集生成的<strong>时间点快照</strong>（point-to-time snapshot）。</p>
<p>它<strong>以紧缩的二进制文件保存Redis数据库某一时刻所有数据对象的内存快照</strong>，可用于Redis的数据<strong>备份</strong>、<strong>转移</strong>与<strong>恢复</strong>。到目前为止，仍是官方的默认支持方案。</p>
<h2 id="触发条件"><a href="#触发条件" class="headerlink" title="触发条件"></a>触发条件</h2><p>触发的方式分为手动和自动。手动触发容易理解，是指我们通过Redis客户端人为的对Redis服务端发起持久化备份指令，然后Redis服务端开始执行持久化流程，这里的指令有save和bgsave。自动触发是Redis根据自身运行要求，在满足预设条件时自动触发的持久化流程。</p>
<h3 id="自动触发"><a href="#自动触发" class="headerlink" title="自动触发"></a>自动触发</h3><ul>
<li>serverCron中<code>save m n</code>配置规则自动触发；</li>
<li>从节点全量复制时，主节点发送rdb文件给从节点完成复制操作，主节点会出发bgsave；</li>
<li>执行<code>debug reload</code>命令重新加载redis时；</li>
<li>默认情况下（未开启AOF）执行shutdown命令时，自动执行bgsave；</li>
</ul>
<p><strong>save规则及检查</strong></p>
<p><code>serverCron</code>是Redis内的一个周期性函数，每隔100毫秒执行一次，它的其中一项工作就是：根据配置文件中save规则来判断当前需要进行自动持久化流程，如果满足条件则尝试开始持久化。</p>
<p><code>save m n</code>的意思是：m秒内有n条写入就触发一次快照，即备份一次。save参数可以配置多组，满足在不同条件的备份要求。如果需要关闭RDB的自动备份策略，可以使用<code>save &quot;&quot;</code>。以下为几种配置的说明：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"># 表示900秒（15分钟）内至少有1个key的值发生变化，则执行</span><br><span class="line">save 900 1</span><br><span class="line"># 表示300秒（5分钟）内至少有1个key的值发生变化，则执行</span><br><span class="line">save 300 10</span><br><span class="line"># 表示60秒（1分钟）内至少有10000个key的值发生变化，则执行</span><br><span class="line">save 60 10000</span><br><span class="line"># 该配置将会关闭RDB方式的持久化</span><br><span class="line">save &quot;&quot;</span><br></pre></td></tr></table></figure>






<h3 id="手动触发"><a href="#手动触发" class="headerlink" title="手动触发"></a>手动触发</h3><ol>
<li><code>save</code></li>
<li><code>bgsave</code></li>
<li><code>flushall</code></li>
</ol>
<p>总结如下图所示：</p>
<p><img src="/2021/12/01/Redis-%E6%8C%81%E4%B9%85%E5%8C%96%E6%9C%BA%E5%88%B6/durable.png"></p>
<h2 id="持久化原理"><a href="#持久化原理" class="headerlink" title="持久化原理"></a>持久化原理</h2><p>在Redis内完成RDB持久化的方法有rdbSave和rdbSaveBackground两个函数方法（源码文件rdb.c中），先简单说下两者差别：</p>
<ul>
<li><code>rdbSave</code>：是同步执行的，方法调用后就会立刻启动持久化流程。由于Redis是单线程模型，持久化过程中会阻塞，Redis无法对外提供服务；</li>
<li><code>rdbSaveBackground</code>：是后台（异步）执行的，该方法会fork出子进程，真正的持久化过程是在子进程中执行的（调用rdbSave），主进程会继续提供服务；</li>
</ul>
<ol>
<li><p>第一种情况不可取，持久化备份会导致短时间内Redis服务不可用，这<strong>对于高可用的系统来讲是无法容忍的</strong>。</p>
</li>
<li><p>第二种方式是RDB持久化的主要实践方式。由于fork子进程后，父进程数据一直在变化，子进程并不与父进程同步，<strong>RDB持久化必然无法保证实时性</strong>；RDB持久化完成后发生断电或宕机，会导致部分数据丢失；<strong>备份频率决定了丢失数据量的大小</strong>，提高备份频率，意味着fork过程消耗较多的CPU资源，也会导致较大的磁盘I&#x2F;O。</p>
</li>
</ol>
<h3 id="Fork-和-CopyOnWrite"><a href="#Fork-和-CopyOnWrite" class="headerlink" title="Fork 和 CopyOnWrite"></a>Fork 和 CopyOnWrite</h3><p>Redis默认会把快照存储文件存储在当前进程的工作目录中的dump.rdb文件中，可以用配置<code>dir</code>和<code>dbfilename</code>配置存储路径和文件名。</p>
<p><strong>快照的过程</strong></p>
<ol>
<li>Redis使用<code>fork</code>函数创建一个子进程复制一份当前进程的副本——子进程；</li>
<li>父进程继续接受并处理客户端发来的命令，而子进程开始将内存中的数据写入硬盘中临时文件。</li>
<li>当子进程写入完所有数据后会用该临时文件替换旧的RDB备份文件，至此一次备份完成。</li>
</ol>
<blockquote>
<p>注意，这里使用到了 Unix中 CoW，也就是写时复制技术。具体的，这里就不介绍了。</p>
</blockquote>
<p>需要注意的是，如果父进程内存占用过大，fork过程会比较耗时，在这个过程中父进程无法对外提供服务；</p>
<p>另外，需要综合考虑计算机内存使用量，fork子进程后会占用双倍的内存资源，需要确保内存够用。</p>
<p>通过<code>info stats</code>(在redis-cli中查看)命令查看latest_fork_usec选项，可以获取最近一个fork以操作的耗时。</p>
<h3 id="rdbSaveBackground"><a href="#rdbSaveBackground" class="headerlink" title="rdbSaveBackground"></a>rdbSaveBackground</h3><p>rdbSaveBackground是RDB持久化的辅助性方法，主要工作是fork子进程，然后根据调用方（父进程或者子进程）不同，有两种不同的执行逻辑。</p>
<ul>
<li>如果调用方是父进程，则fork出子进程，保存子进程信息后直接返回。</li>
<li>如果调用方是子进程则调用rdbSave执行RDB持久化逻辑，持久化完成后退出子进程。</li>
</ul>
<figure class="highlight c"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">int</span> <span class="title function_">rdbSaveBackground</span><span class="params">(<span class="type">char</span> *filename, rdbSaveInfo *rsi)</span> &#123;</span><br><span class="line">    <span class="type">pid_t</span> childpid;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (hasActiveChildProcess()) <span class="keyword">return</span> C_ERR;</span><br><span class="line"></span><br><span class="line">    server.dirty_before_bgsave = server.dirty;</span><br><span class="line">    server.lastbgsave_try = time(<span class="literal">NULL</span>);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// fork子进程</span></span><br><span class="line">    <span class="keyword">if</span> ((childpid = redisFork(CHILD_TYPE_RDB)) == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="type">int</span> retval;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/* Child 子进程：修改进程标题 */</span></span><br><span class="line">        redisSetProcTitle(<span class="string">&quot;redis-rdb-bgsave&quot;</span>);</span><br><span class="line">        redisSetCpuAffinity(server.bgsave_cpulist);</span><br><span class="line">        <span class="comment">// 执行rdb持久化</span></span><br><span class="line">        retval = rdbSave(filename,rsi);</span><br><span class="line">        <span class="keyword">if</span> (retval == C_OK) &#123;</span><br><span class="line">            sendChildCOWInfo(CHILD_TYPE_RDB, <span class="number">1</span>, <span class="string">&quot;RDB&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 持久化完成后，退出子进程</span></span><br><span class="line">        exitFromChild((retval == C_OK) ? <span class="number">0</span> : <span class="number">1</span>);</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">/* Parent 父进程：记录fork子进程的时间等信息*/</span></span><br><span class="line">        <span class="keyword">if</span> (childpid == <span class="number">-1</span>) &#123;</span><br><span class="line">            server.lastbgsave_status = C_ERR;</span><br><span class="line">            serverLog(LL_WARNING,<span class="string">&quot;Can&#x27;t save in background: fork: %s&quot;</span>,</span><br><span class="line">                strerror(errno));</span><br><span class="line">            <span class="keyword">return</span> C_ERR;</span><br><span class="line">        &#125;</span><br><span class="line">        serverLog(LL_NOTICE,<span class="string">&quot;Background saving started by pid %ld&quot;</span>,(<span class="type">long</span>) childpid);</span><br><span class="line">        <span class="comment">// 记录子进程开始的时间、类型等。</span></span><br><span class="line">        server.rdb_save_time_start = time(<span class="literal">NULL</span>);</span><br><span class="line">        server.rdb_child_type = RDB_CHILD_TYPE_DISK;</span><br><span class="line">        <span class="keyword">return</span> C_OK;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> C_OK; <span class="comment">/* unreached */</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<h3 id="rdbSave"><a href="#rdbSave" class="headerlink" title="rdbSave"></a>rdbSave</h3><p><code>rdbSave</code>是真正执行持久化的方法，它在执行时存在大量的I&#x2F;O、计算操作，耗时、CPU占用较大，<strong>在Redis的单线程模型中持久化过程会持续占用线程资源</strong>，<strong>进而导致Redis无法提供其他服务</strong>。</p>
<p>为了解决这一问题Redis在rdbSaveBackground中fork出子进程，由子进程完成持久化工作，避免了占用父进程过多的资源。</p>
<p>整体流程可以总结为：创建并打开临时文件、Redis内存数据写入临时文件、临时文件写入磁盘、临时文件重命名为正式RDB文件、更新持久化状态信息（dirty、lastsave）。其中“Redis内存数据写入临时文件”最为核心和复杂，写入过程直接体现了RDB文件的文件格式，本着一图胜千言的理念，我按照源码流程绘制了下图。</p>
<img src="rdbsave.png" style="zoom:30%;" />







<h1 id="AOF"><a href="#AOF" class="headerlink" title="AOF"></a>AOF</h1><p>RDB是一种时间点（point-to-time）快照，适合数据备份及灾难恢复，由于工作原理的“先天性缺陷”无法保证实时性持久化，这对于缓存丢失零容忍的系统来说是个硬伤，于是就有了AOF。</p>
<h2 id="AOF原理"><a href="#AOF原理" class="headerlink" title="AOF原理"></a>AOF原理</h2><p>AOF是Append Only File的缩写，它是Redis的完全持久化策略，从1.1版本开始支持；这里的file存储的是引起Redis数据修改的命令集合（比如：set&#x2F;hset&#x2F;del等）。</p>
<p>这些集合按照Redis Server的处理顺序追加到文件中。当重启Redis时，Redis就可以从头读取AOF中的指令并重放，进而恢复关闭前的数据状态。</p>
<p>AOF持久化默认是关闭的，修改redis.conf以下信息并重启，即可开启AOF持久化功能。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"># no-关闭，yes-开启，默认no</span><br><span class="line">appendonly yes</span><br><span class="line">appendfilename appendonly.aof</span><br></pre></td></tr></table></figure>

<p>AOF本质是为了持久化，持久化对象是Redis内每一个key的状态，持久化的目的是为了在Reids发生故障重启后能够恢复至重启前或故障前的状态。相比于RDB，AOF采取的策略是按照执行顺序持久化每一条能够引起Redis中对象状态变更的命令，命令是有序的、有选择的。把aof文件转移至任何一台Redis Server，从头到尾按序重放这些命令即可恢复如初。</p>
<p>最本质的原理用<strong>命令重放</strong>四个字就可以概括。</p>
<p>但是，考虑实际生产环境的复杂性及操作系统等方面的限制，Redis所要考虑的工作要比这个例子复杂的多。</p>
<h2 id="持久化流程"><a href="#持久化流程" class="headerlink" title="持久化流程"></a>持久化流程</h2><p>从流程上来看，AOF的工作原理可以概括为几个步骤：</p>
<p>命令追加（append）、文件写入与同步（fsync）、文件重写（rewrite）、重启加载（load）。</p>
<p><img src="/2021/12/01/Redis-%E6%8C%81%E4%B9%85%E5%8C%96%E6%9C%BA%E5%88%B6/aof.png"></p>
<h3 id="命令追加"><a href="#命令追加" class="headerlink" title="命令追加"></a>命令追加</h3><p>当 AOF 持久化功能处于打开状态时，Redis 在执行完一个写命令之后，会以协议格式(也就是RESP，即 Redis 客户端和服务器交互的通信协议 )把被执行的写命令追加到 Redis 服务端维护的 AOF 缓冲区末尾。对AOF文件只有单线程的追加操作，没有seek等复杂的操作，即使断电或宕机也不存在文件损坏风险。</p>
<h3 id="文件写入与同步"><a href="#文件写入与同步" class="headerlink" title="文件写入与同步"></a>文件写入与同步</h3><p>AOF文件的写入与同步离不开操作系统的支持，开始介绍之前，我们需要补充一下Linux I&#x2F;O缓冲区相关知识。硬盘I&#x2F;O性能较差，文件读写速度远远比不上CPU的处理速度，如果每次文件写入都等待数据写入硬盘，会整体拉低操作系统的性能。为了解决这个问题，操作系统提供了延迟写（delayed write）机制来提高硬盘的I&#x2F;O性能。</p>
<blockquote>
<p>传统的UNIX实现在内核中设有缓冲区高速缓存或页面高速缓存，大多数磁盘I&#x2F;O都通过缓冲进行。 当将数据写入文件时，内核通常先将该数据复制到其中一个缓冲区中，如果该缓冲区尚未写满，则并不将其排入输出队列，而是等待其写满或者当内核需要重用该缓冲区以便存放其他磁盘块数据时， 再将该缓冲排入到输出队列，然后待其到达队首时，才进行实际的I&#x2F;O操作。这种输出方式就被称为延迟写。</p>
<p>为了保证磁盘上实际文件系统与缓冲区高速缓存中内容的一致性，UNIX系统提供了<code>sync</code>、<code>fsync</code>和<code>fdatasync</code>三个函数为强制写入硬盘提供支持。</p>
</blockquote>
<p>延迟写减少了磁盘读写次数，但是却降低了文件内容的更新速度，使得欲写到文件中的数据在一段时间内并没有写到磁盘上。当系统发生故障时，这种延迟可能造成文件更新内容的丢失。</p>
<p>Redis每次事件轮训结束前（<code>beforeSleep</code>）都会调用函数<code>flushAppendOnlyFile</code>。</p>
<p><code>flushAppendOnlyFile</code>会把AOF缓冲区（<code>aof_buf</code>）中的数据写入内核缓冲区，</p>
<p>并且根据<code>appendfsync</code>配置来决定采用何种策略把内核缓冲区中的数据写入磁盘，即调用<code>fsync()</code>。该配置有三个可选项<code>always</code>、<code>no</code>、<code>everysec</code>，具体说明如下：</p>
<ul>
<li>always：每次都调用<code>fsync()</code>，是安全性最高、性能最差的一种策略。</li>
<li>no：不会调用<code>fsync()</code>。性能最好，安全性最差。</li>
<li>everysec：仅在满足同步条件时调用<code>fsync()</code>。<strong>这是官方建议的同步策略</strong>，也是默认配置，做到兼顾性能和数据安全性，理论上只有在系统突然宕机的情况下丢失1秒的数据。</li>
</ul>
<p>注意：上面介绍的策略受配置项<code>no-appendfsync-on-rewrite</code>的影响，它的作用是告知Redis：AOF文件重写期间是否禁止调用fsync()，默认是no，即允许允许调用<code>fsync</code>。</p>
<h3 id="文件重写"><a href="#文件重写" class="headerlink" title="文件重写"></a>文件重写</h3><p>如前面提到的，Redis长时间运行，命令不断写入AOF，文件会越来越大，不加控制可能影响宿主机的安全。</p>
<p>为了解决AOF文件体积问题，Redis引入了<strong>AOF文件重写</strong>功能，它会根据Redis内数据对象的最新状态生成新的AOF文件，<strong>新旧文件对应的数据状态一致，但是新文件会具有较小的体积</strong>。重写既减少了AOF文件对磁盘空间的占用，又可以提高Redis重启时数据恢复的速度。</p>
<p><strong>AOF文件太大时会触发AOF文件重写，那到底是多大呢？有哪些情况会触发重写操作呢？</strong></p>
<p>与RDB方式一样，AOF文件重写既可以手动触发，也会自动触发。手动触发直接调用<code>bgrewriteaof</code>命令，如果当时无子进程执行会立刻执行，否则安排在子进程结束后执行。自动触发由Redis的周期性方法<code>serverCron</code>检查在满足一定条件时触发。先了解两个配置项：</p>
<ul>
<li>auto-aof-rewrite-percentage：代表当前AOF文件大小（aof_current_size）和上一次重写后AOF文件大小（aof_base_size）相比，增长的比例。</li>
<li>auto-aof-rewrite-min-size：表示运行<code>BGREWRITEAOF</code>时AOF文件占用空间最小值，默认为64MB；</li>
</ul>
<p>Redis启动时把<code>aof_base_size</code>初始化为当时aof文件的大小，Redis运行过程中，当AOF文件重写操作完成时，会对其进行更新；<code>aof_current_size</code>为<code>serverCron</code>执行时AOF文件的实时大小。当满足以下两个条件时，AOF文件重写就会触发：</p>
<ol>
<li><p>增长比例：(aof_current_size - aof_base_size) &#x2F; aof_base_size &gt; auto-aof-rewrite-percentage</p>
</li>
<li><p>文件大小：aof_current_size &gt; auto-aof-rewrite-min-size</p>
</li>
</ol>
<h3 id="混合持久化方案"><a href="#混合持久化方案" class="headerlink" title="混合持久化方案"></a>混合持久化方案</h3><p>除了纯AOF的方式，还有RDB+AOF方式。</p>
<p>这一策略由配置参数<code>aof-use-rdb-preamble</code>（使用RDB作为AOF文件的前半段）控制，设置为yes可开启。所以，在AOF重写过程中文件的写入会有两种不同的方式。</p>
<ul>
<li>no：按照AOF格式写入命令，与4.0前版本无差别；</li>
<li>yes：先按照RDB格式写入数据状态，然后把重写期间AOF缓冲区的内容以AOF格式写入，文件前半部分为RDB格式，后半部分为AOF格式。</li>
</ul>
<h3 id="重启加载"><a href="#重启加载" class="headerlink" title="重启加载"></a>重启加载</h3><p>Redis启动后通过<code>loadDataFromDisk</code>函数执行数据加载工作。这里需要注意，虽然持久化方式可以选择AOF、RDB或者两者兼用，但是<strong>数据加载时必须做出选择</strong>，两种方式各自加载一遍就乱套了。</p>
<p>理论上，AOF持久化比RDB具有更好的实时性，当<strong>开启了AOF持久化方式，Redis在数据加载时优先考虑AOF方式</strong>。而且，Redis 4.0版本后AOF支持了混合持久化，加载AOF文件需要考虑版本兼容性。Redis数据加载流程如下图所示：</p>
<p><img src="/2021/12/01/Redis-%E6%8C%81%E4%B9%85%E5%8C%96%E6%9C%BA%E5%88%B6/aof-info.png"></p>
<p>在AOF方式下，开启混合持久化机制生成的文件是<strong>RDB头+AOF尾</strong>，未开启时生成的文件全部为AOF格式。</p>
<p>考虑两种文件格式的兼容性，如果Redis发现AOF文件为RDB头，会使用RDB数据加载的方法读取并恢复前半部分；然后再使用AOF方式读取并恢复后半部分。由于AOF格式存储的数据为RESP协议命令，Redis采用伪客户端执行命令的方式来恢复数据。</p>
<p>如果在AOF命令追加过程中发生宕机，由于延迟写的技术特点，AOF的RESP命令可能不完整（被截断）。</p>
<p>遇到这种情况时，Redis会按照配置项<code>aof-load-truncated</code>执行不同的处理策略。这个配置是告诉Redis启动时读取aof文件，如果发现文件被截断（不完整）时该如何处理：</p>
<ul>
<li>yes：则尽可能多的加载数据，并以日志的方式通知用户；</li>
<li>no：则以系统错误的方式崩溃，并禁止启动，需要用户修复文件后再重启。</li>
</ul>
<p><img src="/2021/12/01/Redis-%E6%8C%81%E4%B9%85%E5%8C%96%E6%9C%BA%E5%88%B6/aof+rdb.png"></p>
<h1 id="RDB-vs-AOF"><a href="#RDB-vs-AOF" class="headerlink" title="RDB vs AOF"></a>RDB vs AOF</h1><p>Redis提供了两种持久化的选择：</p>
<ol>
<li>RDB支持以特定的实践间隔为数据集生成时间点快照；</li>
<li>AOF把Redis Server收到的每条写指令持久化到日志中，待Redis重启时通过重放命令恢复数据。日志格式为RESP协议，对日志文件只做append操作，无损坏风险。并且当AOF文件过大时可以自动重写压缩文件。</li>
</ol>
<p>当然，如果你不需要对数据进行持久化，也可以禁用Redis的持久化功能，但是大多数情况并非如此。实际上，我们时有可能<strong>同时使用RDB和AOF两种方式</strong>的，最重要的就是我们要理解两者的区别，以便合理使用。</p>
<h4 id="RDB优点"><a href="#RDB优点" class="headerlink" title="RDB优点"></a>RDB优点</h4><ul>
<li>RDB是一个<strong>格式紧凑压缩</strong>的二进制文件，代表Redis在某一个时间点上的数据快照，非常<strong>适合用于备份、全量复制</strong>等场景。</li>
<li>RDB<strong>对灾难恢复、数据迁移非常友好</strong>，RDB文件可以转移至任何需要的地方并重新加载。</li>
<li>RDB是Redis数据的内存快照，<strong>数据恢复速度较快</strong>，相比于AOF的命令重放有着更高的性能。</li>
</ul>
<h4 id="RDB缺点"><a href="#RDB缺点" class="headerlink" title="RDB缺点"></a>RDB缺点</h4><ul>
<li>RDB方式<strong>无法做到实时或秒级持久化</strong>。因为持久化过程是通过fork子进程后由子进程完成的，子进程的内存只是在fork操作那一时刻父进程的数据快照，而fork操作后父进程持续对外服务，内部数据时刻变更，子进程的数据不再更新，两者始终存在差异，所以无法做到实时性。</li>
<li>RDB持久化过程中的fork操作，会导致<strong>内存占用加倍（可能）</strong>，而且父进程数据越多，fork过程越长。</li>
<li>Redis请求<strong>高并发可能会频繁命中save规则</strong>，导致fork操作及持久化备份的频率不可控；</li>
<li>RDB文件有文件格式要求，不同版本的Redis会对文件格式进行调整，存在老版本无法兼容新版本的问题。</li>
</ul>
<h4 id="AOF优点"><a href="#AOF优点" class="headerlink" title="AOF优点"></a>AOF优点</h4><ul>
<li>AOF持久化具有<strong>更好的实时性</strong>，我们可以选择三种不同的方式（<code>appendfsync</code>）：no、every second、always，<strong>every second作为默认的策略</strong>具有最好的性能，极端情况下可能会丢失一秒的数据。</li>
<li>AOF文件只有append操作，无复杂的seek等文件操作，没有损坏风险。即使最后写入数据被截断，也很容易使用<code>redis-check-aof</code>工具修复；</li>
<li>AOF文件以已于理解与解析的方式包含了对Redis中数据的所有操作命令。即使不小心错误的清除了所有数据，只要没有对AOF文件重写，我们就可以通过移除最后一条命令找回所有数据。</li>
<li><strong>AOF已经支持混合持久化</strong>，<strong>文件大小可以有效控制</strong>，并<strong>提高了数据加载时的效率</strong>。</li>
</ul>
<h4 id="AOF缺点"><a href="#AOF缺点" class="headerlink" title="AOF缺点"></a>AOF缺点</h4><ul>
<li>对于相同的数据集合，<strong>AOF文件通常会比RDB文件大</strong>；</li>
<li>在特定的fsync策略下，<strong>AOF会比RDB略慢</strong>。一般来讲，fsync_every_second的性能仍然很高，fsync_no的性能与RDB相当。但是在巨大的写压力下，RDB更能提供最大的低延时保障。</li>
<li>在AOF上，Redis曾经遇到一些几乎不可能在RDB上遇到的罕见bug。一些特殊的指令（如BRPOPLPUSH）导致重新加载的数据与持久化之前不一致，Redis官方曾经在相同的条件下进行测试，但是无法复现问题。</li>
</ul>
<h2 id="使用建议"><a href="#使用建议" class="headerlink" title="使用建议"></a>使用建议</h2><p>对RDB和AOF两种持久化方式的工作原理、执行流程及优缺点了解后，我们来思考下，实际场景中应该怎么权衡利弊，合理的使用两种持久化方式。如果仅仅是使用Redis作为缓存工具，所有数据可以根据持久化数据库进行重建，则可关闭持久化功能，做好预热、缓存穿透、击穿、雪崩之类的防护工作即可。</p>
<p>一般情况下，Redis会承担更多的工作，如分布式锁、排行榜、注册中心等，持久化功能在灾难恢复、数据迁移方面将发挥较大的作用。建议遵循几个原则：</p>
<ul>
<li><strong>不要把Redis作为数据库</strong>，所有数据尽可能可由应用服务自动重建。</li>
<li>使用4.0以上版本Redis，使用AOF+RDB混合持久化功能。</li>
<li><strong>合理规划Redis最大占用内存</strong>，防止AOF重写或save过程中资源不足。</li>
<li>避免单机部署多实例。</li>
<li><strong>生产环境多为集群化部署</strong>，可在slave开启持久化能力，让master更好的对外提供写服务。</li>
<li><strong>备份文件应自动上传至异地机房或云存储</strong>，做好灾难备份。</li>
</ul>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/kismetv/p/9137897.html">深入学习Redis（2）：持久化 </a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/12/01/%E6%95%96%E4%B8%99%E7%9A%84%E6%96%87%E7%AB%A0%E9%93%BE%E6%8E%A5/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/12/01/%E6%95%96%E4%B8%99%E7%9A%84%E6%96%87%E7%AB%A0%E9%93%BE%E6%8E%A5/" class="post-title-link" itemprop="url">敖丙的文章链接</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2021-12-01 00:48:03 / 修改时间：00:48:18" itemprop="dateCreated datePublished" datetime="2021-12-01T00:48:03+08:00">2021-12-01</time>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <p>我会从下图中的知识点去写这个系列，很多细节的点，可能想得不是很完善，大家可以去<a href="#%E5%85%AC%E4%BC%97%E5%8F%B7">【公众号】</a>获取或者加我<a href="#%E5%BE%AE%E4%BF%A1">【微信】</a>提意见(别忘记<strong>Star</strong>哟)。</p>
<p>原创文章每周最少两篇，公众号首发文章，<a target="_blank" rel="noopener" href="https://space.bilibili.com/130763764">【B站】</a>首发视频，比博客早一到两篇。</p>
<p align="center">
    <a href="https://github.com/AobingJava/JavaFamily" target="_blank">
        <img src="https://tva1.sinaimg.cn/large/006y8mN6ly1g98588lrx2j305k05kgm0.jpg" width=""/>
    </a>
</p>



<p align="center">
  <a href="#微信"><img src="https://img.shields.io/badge/weChat-微信群-blue.svg" alt="微信群"></a>
  <a href="#公众号"><img src="https://img.shields.io/badge/%E5%85%AC%E4%BC%97%E5%8F%B7-三太子敖丙-lightgrey.svg" alt="公众号"></a>
  <a target="_blank" rel="noopener" href="https://space.bilibili.com/130763764"><img src="https://img.shields.io/badge/bilibili-哔哩哔哩-critical" alt="投稿"></a>
  <a target="_blank" rel="noopener" href="https://www.toutiao.com/c/user/3270187212/#mid=1557137040287746"><img src="https://img.shields.io/badge/toutiao-头条-9cf" alt="投稿"></a>
  <a target="_blank" rel="noopener" href="https://juejin.im/user/59b416065188257e671b670a"><img src="https://img.shields.io/badge/juejin-掘金-blue.svg" alt="公众号"></a>
  <a target="_blank" rel="noopener" href="https://www.zhihu.com/people/aobingJava/activities"><img src="https://img.shields.io/badge/zhihu-知乎-informational" alt="投稿"></a>
  <a target="_blank" rel="noopener" href="https://me.csdn.net/qq_35190492"><img src="https://img.shields.io/badge/csdn-CSDN-red.svg" alt="投稿"></a>
  <a target="_blank" rel="noopener" href="https://my.oschina.net/javaFamily"><img src="https://img.shields.io/badge/oschina-开源中国-green" alt="投稿"></a>
  <a target="_blank" rel="noopener" href="https://www.cnblogs.com/aobing/"><img src="https://img.shields.io/badge/cnblogs-博客园-important.svg" alt="投稿"></a>
</p>


<p><img src="/%E6%95%96%E4%B8%99%E7%9A%84%E6%96%87%E7%AB%A0%E9%93%BE%E6%8E%A5/0082zybply1gcb2u3keh9j30p02j4h2r.jpg"><br><img src="/%E6%95%96%E4%B8%99%E7%9A%84%E6%96%87%E7%AB%A0%E9%93%BE%E6%8E%A5/007S8ZIlgy1gf8flovpg1j30u07l7hdx.jpg"></p>
<h4 id="后端面试点合集"><a href="#后端面试点合集" class="headerlink" title="后端面试点合集"></a><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/gBr3UfC1HRcw4U-ZMmtRaQ">后端面试点合集</a></h4><h4 id="脑图在线编辑地址"><a href="#脑图在线编辑地址" class="headerlink" title="脑图在线编辑地址"></a><a target="_blank" rel="noopener" href="https://www.processon.com/view/5e86b713e4b0bf3ebcf4e376">脑图在线编辑地址</a></h4><h1 id="目录-善用Ctrl-F"><a href="#目录-善用Ctrl-F" class="headerlink" title="目录(善用Ctrl+F)"></a>目录(善用Ctrl+F)</h1><p><strong>注 : 没链接的是还没写(耐心等待更新吧)</strong></p>
<ul>
<li><p>学习路线</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/co6ZiALBCUHxc-8SpcKxPw">后端技术学些啥</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/mYfVAo8-jkPFwhbqDZyQzw">从毕业到技术专家我做了啥</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/z2S1EjWQDwKm5Ud36IenNw">秒杀系统设计</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/RRHN8t017ofOvb4nvlCStg">全栈秒杀系统设计</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/KWb3POodisbOEsQVblsoGw">秒杀系统2.0</a></p>
</li>
</ul>
</li>
<li><p>Java基础核心串讲</p>
<ul>
<li><p>Java基础知识</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/-xFSHf7Gz3FUcafTJUIGWQ">Java基础面试16问</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/FgBCop2zFfcX5ZszE0NoCQ">零拷贝</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/DEd0VY3dhR6B0hjQSEtB7Q">一次相亲经历，我彻底搞懂了什么叫阻塞非阻塞，同步异步</a></p>
</li>
</ul>
</li>
<li><p>计算机操作系统与Linux</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/dP8AJbvGgvYIXDy3zeXCRg">52图初探Linux通用知识</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/j2msue1Hjcwd9z1U2p0TDQ">图解 | 计算机文件系统</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/11MRrxmiyZybTUbjRDRubQ">浏览器的秘密藏在这31张图里</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/24vBHgtw5efC9V9yYqknNg">大厂面试Linux就这5个问题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/7iQ5jO_qfYbwKdM21qywYg">Linux 内核 vs Windows 内核</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/7bSwKiPmtJbs7FtRWZZqpA">学习linux命令，看这篇2w多字的命令详解就够了</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/TbIPlBtkK_zCyt54cCX8dw">Linux IO 模型</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/eWHQ8co0jF_a26Xz8ZQLjg">带宽、延时、吞吐率、PPS 这些都是啥？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/FaHKGRI69TqDj0AJtNiVoA">进程和线程基础知识全家桶，30 张图一套带走</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/oexktPKDULqcZQeplrFunQ">20 张图揭开内存管理的迷雾</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/59xyIj1nIFp2TtkKFYnR-g">看完这篇操作系统，和面试官扯皮就没问题了。</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/mblyh6XrLj1bCwL0Evs-Vg">进程间通信</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/CGqy0j5WvarN6mTmYB8vSA">进程之间究竟有哪些通信方式？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/fKHOf_CzG8HYXHlg54V_rg">键盘敲入 A 字母时，操作系统期间发生了什么…</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/UT2JrfpkA5OUC9fJJ_MirQ">一口气搞懂「文件系统」，就靠这 25 张图了</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/x-AZQO5uiuu5svIvScotzA">「网络IO套路」当时就靠它追到女友</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/hjywA21UrvZSzdgvAr6fQA">10 张图打开 CPU 缓存一致性的大门</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/TJghsMRqkzBIsoHjGiEwFw">敖丙说了这么多次 I&#x2F;O，可你知道其中的原理么？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/EVequWGVMWV5Ki2llFzdHg">迄今为止把同步&#x2F;异步&#x2F;阻塞&#x2F;非阻塞&#x2F;BIO&#x2F;NIO&#x2F;AIO讲的这么清楚的好文章（快快珍藏）</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/px6-YnPEUCEqYIp_YHhDzg">原来 8 张图，就能学废 Reactor 和 Proactor</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ws6bgcv6dKIIJDwYYliMAg">2.5w字 + 36 张图+一周时间：爆肝操作系统面试题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/uM1ZNT8UwfvhqoscK8FJiQ">计算机时间到底是怎么来的？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/CMWlDywI1zbgJSoeGTBmuw">IO 多路复用</a></p>
</li>
</ul>
</li>
<li><p>计算机网络  </p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/WfcozFAdXbOJ0EIcZEfLVg">计算机网络</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/3FTuMZaUrT9Vw-ZScJkQbg">熬夜整理的20道计算机网络面试题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/drPVkRbCsDIlX6Ls2pDmqA">什么是单点登录(SSO)</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/I6BLwbIpfGEJnxjDcPXc1A">键入网址后，期间发生了什么？</a></p>
</li>
<li><p>HTTP&#x2F;HTTPS</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/AK1Pb9rx0q5Hf8dq6HNOhw">图解HTTP协议</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/21JaXwdfSjItj5SgOwhapg">全网最透彻HTTPS（面试常问）</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ljtDM1d2KgtxAAhwUFSHkg">一文领略 HTTP 的前世今生</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/amOya0M00LwpL5kCS96Y6w">硬核！30 张图解 HTTP 常见的面试题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/t7ZYT6wBBbFYVBPOSztpRg">面试 HTTP ，99% 的面试官都爱问这些问题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Mi3zM-oyMoxxdzSlSnHpQQ">中间人攻击</a></p>
</li>
</ul>
</li>
<li><p>TCP&#x2F;IP    </p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/LUtk6u_zv0w8g8GIGWEuCw">万字长文 | 23 个问题 TCP 疑难杂症全解析</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/jTDU-zxP1INTYLpGLypjXQ">实战！敖丙用“大白鲨”让你看见 TCP</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ytV7RZSyFXyvPW_lKhv8hw">面试官：换人！他连 TCP 这几个参数都不懂</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/lkX2Mb2aCj8ugOaRmVVBqw">TCP&#x2F;IP 基础知识总结</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/tRXlq1hErqKQLMMLcxoXvg">TCP 半连接队列和全连接队列满了会发生什么？又该如何应对？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/NL7Jzh0lYoA395yzaGxBHw">跟着动画学习TCP三次握手和四次挥手</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/rX3A_FA19n4pI9HicIEsXg">敖丙用近 40 张图解被问千百遍的 TCP 三次握手和四次挥手面试题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/HjOUsKn8eLfDogbBX3hPnA">你还在为 TCP 重传、滑动窗口、流量控制、拥塞控制发愁吗？看完图解就不愁了</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/p9oA9YTZLoXVooUOSNQCMg">IP 基础知识“全家桶”，敖丙45 张图一套带走你</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/3KF0IxLum8EOtcF0ZNIiPA">敖丙听说你 ping 用的很 6 ？给我图解一下 ping 的工作原理！</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/BK2H7JQIo6zOrXYg9RcbeA">一文搞定 UDP 和 TCP 高频面试题！</a></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>7种常见的设计模式和使用场景</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/dW0L-PoBeTFHhD29HJO0BQ">单例模式</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/j42vn-A_rdWZP3aixXB8Ag">工厂模式</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/b8xa3o4fKDme2438oJTakw">流程引擎</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/h7iEyxgN34FPn191S8qUpA">建造者模式</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/fORmbucHuPVMS2z3q-mV3A">原型模式</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/l9EdvpAMukIDOgaN7x3b4w">责任链模式</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/_hVGgJxefoVXFHFNXC-U6A">观察者模式</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/AFbt7VJMASenD2S8KbPKJQ">策略模式</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/HOnQf-S-w6A19tGnoIXBrg">策略模式2.0</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/oGm4ET-NDp4BSVi0p2MZxA">模板方法</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/QHgg4CcU6VlA0gtzzQ_JLQ">迭代器模式</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/dtmeTLsML6TYM8F8ch2FGQ">命令模式&amp;中介者模式</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/a1kVL71OlUwZJrsyCE0I-A">对象池模式&amp;解释器模式</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/lNw1yjn_xMOLpzbunaS10A">代理模式</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/O0sIOyE2rgTPyj54_QRx_g">使用Java8改造后的模板方法模式真的是yyds</a></p>
</li>
</ul>
</li>
<li><p>Java必会基础与新版本特性</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/U5FkL9zyzr0PkAp8MJJ8BQ">枚举</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Du2huBEkI7IR3noPeK_67g">从String中移除空白字符的多种方式！？差别竟然这么大！</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/6nJ-6cDLW6TfysWV5ZB3Iw">双亲委派原则</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/-NQoKpBTXwGUxd2hUbCU2Q">Java 16 新特征，真香！</a></p>
</li>
</ul>
</li>
<li><p>线上问题排查</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/8YBJn8x7Sf9dai9jNkX6Tg">线上问题如何排查？</a></li>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/TnLl2OW9XJLSZihcpgP7VQ">RT 过长，排查思路</a></li>
</ul>
</li>
</ul>
</li>
<li><p>集合框架</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/bVOSat47L0Hskfx9akAN6Q">Java 集合框架看这一篇就够了</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/eSCVJclB-IaJYkPKsWSkRA">Java 集合中「堆」是啥？</a></p>
</li>
<li><p>HashMap</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/0Gf2DzuzgEx0i3mHVvhKNQ">HashMap</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/VtIpj-uuxFj5Bf6TmTJMTw">面试官：HashMap 为什么线程不安全？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ktre8-C-cP_2HZxVW5fomQ">万万没想到，HashMap默认容量的选择，竟然背后有这么多思考！？</a></p>
</li>
</ul>
</li>
<li><p>ConcurrentHashMap</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/AixdbEiXf3KfE724kg2YIw">ConcurrentHashMap &amp; Hashtable（文末送书）</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/My4P_BBXDnAGX1gh630ZKw">我就知道面试官接下来要问我 ConcurrentHashMap 底层原理了</a></p>
</li>
</ul>
</li>
<li><p>ArrayList</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/WoGclm7SsbURGigI3Mwr3w">ArrayList</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/3PNWmtS-bEZgZjd9wyMiDA">ArrayList源码解析，老哥，来一起复习一哈？</a></p>
</li>
</ul>
</li>
<li><p>Vector  </p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/0cMrE87iUxLBw_qTBMYMgA">面试官问我同步容器（如Vector）的所有操作一定是线程安全的吗？我懵了！</a></li>
</ul>
</li>
</ul>
</li>
<li><p>深入浅出JVM</p>
<ul>
<li><p>JVM内存模型</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/fit90VdZUa2pG9lbET0i7w">【JVM故事】了解JVM的结构，好在面试时吹牛</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Wws24Fhg1nH4dHvtcFYi2g">Java堆内存是线程共享的！面试官：你确定吗？</a></p>
</li>
</ul>
</li>
<li><p>性能调优、线上问题排查</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/P8s3kuceBNovUP5adXpFCQ">线上服务的FGC问题排查，看这篇就够了！</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/yutHXOi6Xl3-Qn91Pvg9wA">记一次大促期间JVM堆外内存泄漏故障排查记录</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/7XGD-Z3wrThv5HyoK3B8AQ">怎么排查堆内存溢出啊？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/roEMz-5tzBZvGxbjq8NhOQ">CPU100%，排查</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/LRx9tLtx1tficWPvUWUTuQ">排查YGC问题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/nWghy4McYx6Ix3QPSLSmkQ">CPU飙高排查</a></p>
</li>
</ul>
</li>
<li><p>类加载机制详解</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/eHqFONXXNc-LD4ugaKM6UA">JVM必问知识点:类加载过程</a></li>
</ul>
</li>
<li><p>垃圾回收机制</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/_AKQs-xXDHlk84HbwKUzOw">看完这篇垃圾回收，和面试官扯皮没问题了</a></li>
</ul>
</li>
<li><p>垃圾回收器、垃圾回收算法</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/nY6vL5MlUXY1lfnIvNHMnw">垃圾回收底层原理</a></li>
</ul>
</li>
</ul>
</li>
<li><p>并发与多线程</p>
<ul>
<li><p>多线程基础知识</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/GDno-X1N8zc98h9MZ8_KoA">可重入锁</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/TsENp5ygFdE0_REgz5Y3Ew">(并发问题的根源到底是什么？)</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/g_eWT05_38AURchMIvNSnA">敖丙稳住了多线程翻车的现场</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/NZAJWBiFvGzCZXjr0KC9Dw">2w字 + 40张图带你参透并发编程！</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/XCR93HSm_E0c3KDcmZk3cA">图解Java中那18 把锁</a></p>
</li>
</ul>
</li>
<li><p>常见关键字  </p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/2ka1cDTRyjsAGk_-ii4ngw">死磕Synchronized源码</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Oa3tcfAFO9IgsbE22C5TEg">面试官想到，一个Volatile，敖丙都能吹半小时</a></p>
</li>
</ul>
</li>
<li><p>多线程锁机制</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/WtAdXvaRuBZ-SXayIKu1mA">乐观锁、悲观锁</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/6QrQ0TZVqSQq26Rms0_mvA">互斥锁、自旋锁、读写锁、悲观锁、乐观锁的应用场景</a></p>
</li>
</ul>
</li>
<li><p>线程池知识点</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/TQGtNpPiTypeKd5kUnfxEw">记一次线程池引发的故障 排查下来是三歪的锅</a></li>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/NDOx94yY06OnHjrYq2lVYw">深入线程池，连环17问</a></li>
</ul>
</li>
<li><p>常见的JUC工具类</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/hB5ncpe7_tVovQj1sNlDRA">AQS</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/trsjgUFRrz40Simq2VKxTA">我画了35张图就是为了让你深入 AQS</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/iNz6sTen2CSOdLE0j7qu9A">1.5w字，30图带你彻底掌握 AQS！</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Gf4MiHPz8DynY80UmwH04Q">TheadLocal 引起的内存泄露故障分析</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/TDw7GnzDw5FK3RWwkIzzZA">终于有人把 CountDownLatch，CyclicBarrier，Semaphore 说明白了!</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/LzkZXPtLW2dqPoz3kh3pBQ">Java面试必问：ThreadLocal</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/gjfeayR36vDAr3FAVd4w4g">StampedLock</a></p>
</li>
</ul>
</li>
<li><p>多线程经典面试题</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/PrUa0tFyu3UZllP2FRDyVA">【面试】如果把线程当作一个人来对待，所有问题都瞬间明白了</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/cdHfTTvMpH60SwG2bjTMBw">Java 并发进阶常见面试题总结</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/WDeewsvWUEBIuabvVVhweA">如果你这样回答“什么是线程安全”，面试官都会对你刮目相看（建议珍藏）</a></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>常用工具集</p>
<ul>
<li><p>JVM问题排查工具-JMC</p>
</li>
<li><p>IDEA开发神器</p>
</li>
<li><p>线上调试神器-btrace</p>
</li>
<li><p>Git原理与工作流</p>
</li>
<li><p>Linux常用分析工具</p>
</li>
</ul>
</li>
<li><p>数据结构与算法</p>
<ul>
<li><p>从二叉搜索树到B+树</p>
</li>
<li><p>经典问题之字符串</p>
</li>
<li><p>经典问题之TOPK</p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Yx3uRVGFYikx8gm-oi-0vg">最大公共子串</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/1dG8Dk7nZeIGHQ0Hp8gq7Q">动态规划</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/B9MVq1617YgtgBtwvhVg-Q">大厂面试爱问的「调度算法」，20 张图一举拿下</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/-8JFh5iLr88XA4AJ9mMf6g">图解红黑树</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/_DJgsdhY4drJyEPN9xZX6Q">面试必备 | 不可不会的反转链表</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/sPIE54UmvNgINZIATQKyew">红黑树【图解】</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/pWXxg9O5BwcC9Zi68PT3TQ">算法学习工具网站</a></p>
</li>
</ul>
</li>
<li><p>必会框架</p>
<ul>
<li><p>Spring全家桶以及源码分析</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/WpRSitDqtgOuU9GnI1-HDw">一分钟带你玩转 Spring IoC</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/NXZp8a3n-ssnC6Y1Hy9lzw">AOP 面试造火箭事件始末</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/qlAvW10TRNVak1oJyHO39Q">SpringAOP面试题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/IglQITCkmx7Lpz60QOW7HA">Spring事务传播行为</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/5mwkgJB7GyLdKDgzijyvXw">什么是 Spring 循环依赖？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/wcK2qsZxKDJTLIGqEIyaNg">Spring常见问题总结（补充版）</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/OMlwHHnGcN7iZ8lerUvW7w">15个经典的Spring面试常见问题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/6YhpbuhdTX9BM_OimoW8MA">敖丙说精通字符串，面试官竟然问Java中的String有没有长度限制！</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/kS0K5P4FdF3v-fiIjGIvvQ">Spring中的循环依赖</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ut3mRwhfqXNjrBtTmI0oWg">Spring容器启动流程</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/u29hMRkfTj_1RHNSukDtEw">Spring循环依赖</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/JcHt99SAbNIlY063rmylpA">我是如何在面试别人Spring事务时“套路”对方的</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/4z9V4-k2Whqiz69gXy5iMQ">SpringIOC 面试题（下）</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/SH4laewpIsio66MUJFLTyg">SpringIOC 面试题（上）</a></p>
</li>
</ul>
</li>
<li><p>SpringCloud </p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/MJrahcDXwxgDr5zBdO3XWw">外行人都能看懂的SpringCloud，错过了血亏！</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/pGSx8eKFH3YnUos3SM2ITw">20000 字的 Spring Cloud 总结</a></p>
</li>
</ul>
</li>
<li><p>分布式框架基石-RPC</p>
<ul>
<li>Dubbo<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/FPbu8rFOHyTGROIV8XJeTA">Dubbo基础</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ISiN06QynyE2pPtX3cGQ9w">Dubbo的服务暴露过程</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/9oDy1OPcfDaEhKD4eNUdOA">Dubbo的服务引用过程</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/oNR9v_ID2oAlEvDI93hRcw">Dubbo服务调用过程</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/gwWOsdQGEN0t2GJVMQQexw">Dubbo的SPI机制是啥？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/-IkHNAM4B0R_j50LkQunig">Dubbo集群容错负载均衡</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/FwL6qArqYc2ENymXk1eZFQ">Dubbo面试题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/2qSA6aJn6KRXrATVE44k0w">1w+字的 Dubbo 面试题&#x2F;知识点总结！（2021 最新版）</a></p>
</li>
</ul>
</li>
<li>RPC实践<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/pkWkD1VhMxhZPRrybLcQjA">敖丙RPC的超时设置，一不小心搞了线上事故</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/_5YMfQK1tmYbmRMldBPlaQ">敖丙找出Dubbo源码BUG，三歪夸了我一天</a></p>
</li>
</ul>
</li>
<li>Netty<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/I9PGsWo7-ykGf2diKklGtA">高性能NIO框架-Netty</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/eJ-dAtOYsxylGL7pBv7VVA">Netty常见面试题总结</a></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>ORM框架Mybatis源码分析</p>
</li>
<li><p>Zookeeper</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/gphDLJMO3QcRoN3zkco4EA">什么是Zookeeper</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ZqQHWLfVD1Rz1agmH3LWrg">分布式锁之Zookeeper</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/k0zAExTvMsrugCaNJxs7hQ">Zookeeper之ZAB协议</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/YawG0GqznDHaiHXZuTsCjA">Zookeeper夺命连环9问</a></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>高并发架构基石-缓存</p>
<ul>
<li><p>Redis数据结构、基础知识</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/aOiadiWG2nNaZowmoDQPMQ">Redis基础知识</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/EjDeypra_d9Tfsn-WkJZdw">集群高可用、哨兵、持久化、LRU</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://github.com/AobingJava/JavaFamily/blob/master/docs/redis/%E5%B8%83%E9%9A%86%E8%BF%87%E6%BB%A4%E5%99%A8(BloomFilter).md">布隆过滤器(BloomFilter)</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/49hgH3COla3wU0rgyiUVgg">Redis—分布式锁深入探究</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/NOsXdrMrWwq4NTm180a6vw">Redis—跳跃表</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/MT1tB2_7f5RuOxKhuEm1vQ">Redis—5种基本数据结构</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/O_qDco6-Dasu3RomWIK_Ig">Redis—持久化</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/9dtGe3d_mbbxW5FpVPDNow">Reids—神奇的HyperLoglog解决统计问题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Z_xriP-jc2Bnmdcm0l5xzg">Redis分布式锁</a> </p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/VY31lBOSggOHvVf54GzvYw">Redis数据结构底层系列-SDS</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/eSx4aL7iaMZlW0cPZswghA">短小精悍之 Redis 命令行工具有趣的罕见用法</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/RnSokJxYxYDeenOP_JE3fQ">Redis分布式锁(全)</a></p>
</li>
</ul>
</li>
<li><p>Redis常见线上故障及其解决方案</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/knz-j-m8bTg5GnKc7oeZLg">缓存击穿、雪崩、穿透</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/BdwZViiAqnFhCde4ZsxwPg">布隆过滤器实战【防止缓存击穿】</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/2hTgP3MRTVDxmmoUFhOaGw">分布式锁、并发竞争、双写一致性</a></p>
</li>
</ul>
</li>
<li><p>Redis常见面试题</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/LkIcGS9kFTXNLFlxASPYUA">Redis面试题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/R1TJMo2IbPUUMox9OAAafQ">Redis常见面试题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/rw42cFbJXwPtsGiqkFErfw">Redis为什么变慢了？一文讲透如何排查Redis性能问题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/mscKInWNAuhCbg183Um9_g">Redis不是一直号称单线程效率也很高吗，为什么又采用多线程了？</a> </p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/dYvM8_6SQnYRB6KjPsprbw">缓存一致性问题怎么解决？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/-caMTrOXQu-o0O44e6I9dQ">内存耗尽后Redis会发生什么？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/vXBFscXqDcXS_VaIERplMQ">妈妈再也不担心我面试被Redis问得脸都绿了</a></p>
</li>
</ul>
</li>
<li><p>知识点之外的缓存之路</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/D4Ik6lTA_ySBOyD3waNj1w">缓存和数据库一致性问题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Ls-Bld0Q6OQ1VD_az3iqbg">一个架构师的缓存修炼之路</a>  </p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/u3Ws1FGhJVIJdpM8IHI7OQ">再见了Antirez我永远的神</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/xS2dqi5IG10AF7cHbzguAA">敖丙在蘑菇街的redis技术分享</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/lLTbL5YW0gIJu9Yx2T2OtA">课代表总结</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/8EN9vrZMhCK-40o_OdnOlg">Redis最佳实践：7个维度+43条使用规范，带你彻底玩转Redis</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/XxY3b5FoVXCvHJWMxQH29g">布隆过滤器过时了，未来属于布谷鸟过滤器？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/5NOTLJ6AM3QJfhvXMSR-MA">什么鬼，面试官竟然让敖丙用Redis实现一个消息队列！！？</a></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>消息队列</p>
<ul>
<li>消息队列基础知识<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Qhw4oS0OeN1N7uT1z6rbqg">消息队列基础知识</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/OKon95MRUqDc9IwtEqPSjQ">消息重复消费、分布式事务、顺序消费</a></p>
</li>
</ul>
</li>
<li>Kafka<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/-IPfWPS1WQMEgcIu0Ak2VQ">Kafka架构与原理</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/kMIhPW2uLdy-mgS9sF6agw">Kafka性能篇：为何Kafka这么”快”？</a></p>
</li>
</ul>
</li>
<li>RocketMQ    <ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/y-4TVwbc7AFGEA7q-_OkYw">RocketMQ</a></li>
</ul>
</li>
<li>消息队列面试题  <ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/1r1x-Irbatvzdc90haaecA">面试题：如何保证消息不丢失？处理重复消息？消息有序性？消息堆积处理？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/68XX9qnEvDTCOw8gYLNBxQ">面试官：你对Kafka比较熟？ 那说说kafka日志段如何读写的吧？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ejZBAGI7qLE_QYSe-AqipA">师兄大厂面试遇到面试官的 Kafka 暴击三连问，快面哭了！</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/S_0YCwUtyQqeBml02jPYqg">消息队列之推还是拉，RocketMQ 和 Kafka是如何做的？</a></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>数据库</p>
<ul>
<li>MySQL基础知识<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/J3kCOJwyv2nzvI0_X0tlnA">国庆肝了8天整整2W字的数据库知识点</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/pinOFeF09orQCnIp4L6XyA">MySQL大表怎么DDL变更</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/eEWMSTAUF1H-gFBx26jujw">MySql主从复制，从原理到实践！</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/LZBctWNWi3qehb-dgUCmxQ">MySQL查询缓存</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/wr2gJGQSA8QH_lmPh1XOkw">MySQL 的 InnoDB 存储引擎是怎么设计的？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ygvuP35B_sJAlBHuuEJhfg">你怎么连模糊匹配like %%怎么优化都不知道</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/NDL1Q6nqdPq5oMBWSpq4ug">数据库基础知识</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/vwHkXGNCdRdx8k7BeBMy9w">MySQL面试夺命20问</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/3d66-yXVZoDFRG1wyRw7kA">mysql 的一行记录是怎么存储的？</a></p>
</li>
</ul>
</li>
<li>索引、锁机制<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/_9rDde9wRYoZeh07EASNQQ">数据库索引</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/faOaXRQM8p0kwseSHaMCbg">一口气搞懂MySQL索引所有知识点</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/fShA7jxjshKyHEHfVDrLVA">你都是如何设计索引的？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/7TPVOT7sloDUKmhldf9uvg">MySQL的索引是怎么加速查询的？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/87qsrj-_hG54uxcOlFr35Q">索引下推</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/1LGJjbx_n_cvZndjM3R8mQ">MySQL 的全局锁、表锁和行锁</a></p>
</li>
</ul>
</li>
<li>事务特性、隔离级别、数据安全<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/rQDitOOnLpLEoj8M11NHwg">MySQL安全方案</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/dQFSrXEmgBMh1PW835rlwQ">数据库连接池到底应该设多大？这篇文章可能会颠覆你的认知</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/yo_LJ6IwGnjiBXSA7ZHdOA">数据库设计</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/L0XUMHInnwN9gSYGH2nzdg">漫话：如何给女朋友解释什么是撞库、脱库和洗库？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/0-YEqTMd0OaIhW99WqavgQ">MVCC和事务隔离级别的关系</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/CZHuGT4sKs_QHD_bv3BfAQ">MySQL事务与MVCC如何实现的隔离级别</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/iFmvvt4DJ-_qFeb0XUh6QA">数据库事务</a></p>
</li>
</ul>
</li>
<li>数据库性能调优<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/7dpNkLaglIyb_9DKdH43eQ">为什么MySQL不建议使用delete删除数据？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/nEmN4S9JOTVGj5IHyfNtCw">工作以来总结的大厂SQL调优姿势</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/oVjm_iLvbkximBjHv9d8ig">为啥阿里巴巴不建议MySQL使用Text类型？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/-gmAPfiKMNJgHhIZqR2C4A">用对了这些场景下的索引，技术总监夸我棒</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/qESZSzHoxUKQRJhb1EQA_Q">MySQL索引凭什么让查询效率提高这么多？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/e0CqJG2-PCDgKLjQfh02tw">MySQL调优</a></p>
</li>
</ul>
</li>
<li>分库分表<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/-Jipxjwe-jAax4hJSe-9Jg">我们为什么要分库分表？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/dMqXBW6W8DeqfAPvko3ViQ">分库分表方案</a></p>
</li>
</ul>
</li>
<li>数据库面试题<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/SqSPxSRslkjUcDasBHGRSQ">innodb是如何插入数据的?</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/qEA1pC2pfh3xz9HuM8iIBg">数据库自增ID用完了会怎么样？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/qHzb6oPrrbAPoIlfLJVNAg">阿里面试：说说一致性读实现原理？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/SNRvdmyS57oWS_CyYKVvSA">我说 SELECT COUNT(*) 会造成全表扫描，面试官让我回去等通知</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Lx4TNPLQzYaknR7D3gmOmQ">原来MySQL面试还会问这些(undo log)</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/izOKK9Ft5wLts8hWIo4ODQ">explain</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/-5N6eg1yMBUvDMbJOXp0eg">你说说一条更新SQL的执行过程？</a></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>大数据</p>
<ul>
<li><p>ODPS离线分析</p>
</li>
<li><p>Hive</p>
</li>
<li><p>Spark</p>
</li>
<li><p>Hadoop</p>
</li>
<li><p>Hbase</p>
</li>
<li><p>HDFS</p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/7rW9eHxdRXtWJExVupcZZQ">大数据体系</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/xa2iGrmyoXq3FtZeU9GU-w">Flink入门教程</a></p>
</li>
</ul>
</li>
<li><p>搜索引擎</p>
<ul>
<li>ElasticSearch<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/AjGiMikSVgCk6INyf-m8hA">Elasticsearch入门</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/F8BsSg6R9LHxKt-gbH_JKQ">记一次害敖丙差点丢工作的线上P0事故</a></p>
</li>
</ul>
</li>
<li>Canal</li>
<li>Kibana</li>
<li>Lucene</li>
<li>Logstash</li>
</ul>
</li>
<li><p>优秀开源框架推荐</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/NgS9tL4IVwGZrssz7fURpA">阿里巴巴开源限流系统 Sentinel 全解析</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/d4K9hJHzZ5IAd2aarsdQrw">支付宝的架构到底有多牛逼？还没看完我就跪了！</a></p>
</li>
</ul>
</li>
<li><p>架构演进之路</p>
<ul>
<li><p>从All in one 到微服务</p>
</li>
<li><p>互联网架构之路</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Pi_fHXhSf17l8q5CZ_U5UA">我难道天生就是个架构师？这可是100k并发啊</a></li>
</ul>
</li>
<li><p>怎么设计一个能顶住双十一的系统？</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/RzJkQuMAszkC8jw8hF-k1w">压测都做些什么?</a></li>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/CxqnaB9cUemvCcWkw2nUcw">千万级、亿级流量怎么处理？</a></li>
</ul>
</li>
<li><p>分布式&amp;分布式事务</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/uv9xYgYrHWqM5acXQYFegA">分布式架构由来</a></li>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/XknegP66mnYboiBx556Kzw">分布式事务</a></li>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ujRRtdLOeKEHsHrtDRNXGA">1.4 w字，25 张图让你彻底掌握分布式事务原理</a></li>
</ul>
</li>
</ul>
</li>
<li><p>互联网前沿技术</p>
<ul>
<li>容器化：Docker与k8s详解</li>
</ul>
</li>
<li><p>面试技巧</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/KeIJDrHbMH_BRUKLnCqExw">敖丙迟早被你们气死</a></p>
</li>
<li><p>简历 </p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/0pNv6pMnenKn1A9PE61VnQ">简历怎么写?</a></li>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/LxVeT49GMKu72PZJ-rDHpA">能不能好好写简历？</a></li>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/tl4DpsQPVh-bvlFzNsrzvw">简历这样写，技术人都不直男了！</a></li>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/NyJ2gLDLIomlgkIFYWVvJg">10个简历小技巧，成为面试战神</a></li>
</ul>
</li>
<li><p>语言组织</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/br7Ak88kA5GkZ26hxwKUiw">自我介绍</a></li>
</ul>
</li>
<li><p>加分项</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/2mdcZnlTWCDYejGkZCdNOQ">面试官喜欢怎样的人</a></li>
</ul>
</li>
<li><p>面试流程</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/jhmpG2bBb4aXoUJzahC0Rg">互联网公司面试流程 注意事项 加分点 防骗指南</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/7Rq8QR58Adi5WGHRlnhDeQ">互联网公司面试流程&amp;加分点，别跟敖丙一样被无良HR坑了</a></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>研发规范</p>
<ul>
<li><p>阿里巴巴技术规范</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/3XX-Lu1KrKEc3IfB9Iqz5A">为什么阿里巴巴禁止使用Apache Beanutils进行属性的copy？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/xvTCaBXkRc7e6dGCUJxRPQ">为什么阿里巴巴禁止开发人员使用isSuccess作为变量名？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/9y89Hy-YnpPjXpcmXpy_GQ">为什么阿里巴巴要求谨慎使用ArrayList中的subList方法</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/fLUf8V0Qahe8piNrRp1UVQ">为什么阿里巴巴不建议在for循环中使用”+”进行字符串拼接</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/JduY7OAeywOlVLbLiX3Xnw">为什么阿里巴巴禁止使用BigDecimal的equals方法做等值比较？</a></p>
</li>
</ul>
</li>
<li><p>开源漏洞    </p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/iV9DbX3LhbBkNiaKLL7PxA">fastjson到底做错了什么？为什么会被频繁爆出漏洞？</a></li>
</ul>
</li>
<li><p>大厂技术研发规范  </p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ROMzeipOwWZWNrVGdVwisg">大厂是如何设计接口的？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ynYi_dMlP2Y9GnQXzQxa3Q">不要再在对外接口中使用枚举类型了！</a></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>面试真题&amp;面经分享</p>
<ul>
<li><p>阿里巴巴面试题</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/oEGSxv5KuNI9BsCxpdi82A">淘宝|蚂蚁|菜鸟|盒马|嘀嘀|饿了么面经(已拿多个offer)</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/gYpZCSYK3YYEvGFFeHdFJQ">互联网寒冬，敖丙挚友7面阿里，终获Offer</a>  </p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/16cZLY9_uJwhi50RJXknIg">一道蚂蚁金服笔试题，还行，中等难度</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/HtLwChoLzqhbM4pKldLDng">一份还热乎的蚂蚁金服面经（已拿Offer）！附答案！！</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/-DZj158-LOQmnCayf1_n3A">我和阿里面试官的一次“邂逅”(附问题详解)</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/WOS6HMdt1-mAGuLiC30_-Q">二本 两年经验读者 阿里P6面经</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/nzvg3e-RgM59zs9WSeUpLg">小姐姐面试蚂蚁金服被虐经历，心疼…</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/jnegDdp4OMSKynZl8OBIpw">一位读者小姐姐的阿里面经分享，快被问哭了！（附部分问题详解）</a> </p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/e6MpOuxeZ53fAatNBhiC9A">朋友怎样刚拿饿了么P7 offer，然后途虎一轮游的</a></p>
</li>
</ul>
</li>
<li><p>字节跳动面试题</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/sWtI7M8K1iajreEo3LtwIw">读者字节、滴滴面经（已拿offer）</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/g6qFbEWfJeahzuzjECuvDg">敖丙读者只复习了半年，怒拿字节跳动Offer</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/xBC1IRr6v8hmIJ9lqCp5pQ">春招字节跳动、蘑菇街四轮面试，分别问了啥？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/0opKiGbKjAfJkRVeVHzpZg">蚂蚁金服2019实习生面经总结(已拿口头offer)</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/hr2pDs2wsiHQuDzW7jmOow">2020 字节跳动后端面经分享！已拿 offer!</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/BiaXzNw5rp69CPPNBRii3g">百度、字节跳动等后台面经，已拿Offer！</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/WOIkWLsA7fyyoHPyNT7HdQ">学妹字节、蘑菇街、阿里、莉莉丝…面经</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/P2lTpC7yQ7VD8if_rMUcPg">双非本科逆袭 拿下ACM金牌、字节和旷视offer、保研985！</a></p>
</li>
</ul>
</li>
<li><p>拼多多面试题</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/kSiLvSrAMwrHLwxvesyjBw">读者一个月拿到阿里字节拼多多offer</a></li>
</ul>
</li>
<li><p>bilibili</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/tMPwanTRC3pj3X4Rx4SeTQ">B站五面面经</a></li>
</ul>
</li>
<li><p>京东</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/-04X2NS165vedIGb85q7QQ">京东二面：为什么需要分布式ID？你项目中是怎么做的？</a></li>
</ul>
</li>
<li><p>美团面试题</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/gSG_ZzZN7Ek2HEGtdi_Nkg">美团面经</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/oAnGGBkre7Z4VkhME-TgzQ">3 年经验的 Java 后端妹子，横扫阿里、滴滴、美团，看完面经我觉得敖丙是垃圾！</a></p>
</li>
</ul>
</li>
<li><p>网易</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/TzaV0dfbWw2zHiF55vq_kA">双非院校非科班的学弟，刚刚拿到了腾讯、网易、美团等大厂Offer</a></li>
</ul>
</li>
<li><p>腾讯面试题</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/EOQafymh7xVtxblstTbQUA">苦修月余，斩获bigo、腾讯offer，面经奉上！</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/yb6MtKMrCl2rAUZVLIfJow">来自朋友最近阿里、腾讯、美团等P7岗位面试题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/0XJHz2seZAw4bDEA2i_i2Q">研二师弟拿下微信 offer</a></p>
</li>
</ul>
</li>
<li><p>国企面试题</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/d4OewL8-gCm_IkqDqufvsQ">怎么面国企或银行？</a></li>
</ul>
</li>
<li><p>外企面试题</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/3_HnVzGm16zU2zhk7BnwFw">Bigo的Java面试，我挂在了第三轮技术面上……</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/yYy5YUeA3VAAQMtnMhaFUQ">我经历的7轮Google面试</a></p>
</li>
</ul>
</li>
<li><p>校招面试题</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/wTKSvziyEXrSyf21iMjhZQ">十道校招常见的面试题</a></li>
</ul>
</li>
<li><p>社招面试题</p>
</li>
<li><p>其他  </p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/VOVUQ2cbYX5UXrrWLIHFXQ">「面经」不小心拿到了B站意向书</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/3OTwlqCpJHEwtUvSQn1xmw">小红书</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/VVonP6MgGRUnBnWa2ukkyw">京东+百度一面，不小心都拿了Offer</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/F2TiFw-LYFXMRj_hsD-TIw">敖丙读者一个月面试近20家大中小厂，在互联网寒冬突破重围，成功上岸阿里云 ！</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/ttKI82_CQdMmxoBw8TS92g">Java后端实习面经，电子科大大三读者投稿！看了之后感触颇深！很开心！</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/AQvDX0n8wBBaWl2OmcpnrA">敖丙8年经验读者，疫情期间面20家大厂总结</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/MkjSYc0JA1-VErX3lJzPmQ">被裁一个月面试的面经和经验分享</a>  </p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/MNm3k1s8CrrI6vF-47cvcg">上岸哈工大！！</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/el7wBmABxzsUng_QLdUtaQ">二本读者实习遭领导歧视，逆袭进百度！</a></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>职场杂谈</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/1rUYxXbb0PaVOkmDQAxHpg">提离职，涨薪 走，别回头</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/lC6F1HVmAGQzXsFbpZz9Qw">我对外包的看法</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/9vPZd1q1vpKuE2qZazLQmA">阿里五年老员工有什么话想对大家说？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Sn7_FcNW7Sq3WVm_LgwVrA">读研or工作？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/XcrBvdlh1At_V42qfQZ9Kw">应届毕业生工作7个月小结</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/HJ9wzgW6TAc6Cf6B8oZEdw">在国企里面当程序猿是一种怎样的体验?</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/enq_fh0hq2gMSgeXfunP6Q">跳槽季，应不应该跳，怎么跳？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/vseVDxl3BjESeZsdKLd1eA">让你自制力提升300%的学习方法</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/D0bxHiXJKU64SinO1mh7Mw">教你在服务器搭建个人面试项目</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/MvA12XM_ZzOG_7ygTmzIfA">程序员只能吃青春饭？30岁后出路在何方？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/HafYjNNoBwuau-MI6W8EnQ">学历对程序员重要么？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/UIbINxc-lEktnXLJhLceZQ">有哪些事情是你成为程序员之后才知道的？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/3VDS3tku0ZYZ4M_v760TEQ">天天加班却不受重用，老板到底喜欢什么样的员工？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/RcL3Y6BZNSBQZJq8V3LCyw">纵使社会虐我千百遍，我依然待它如初恋</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/10DvwjXysmGzLa6AoSsXNw">国家何时整治程序员的高薪现象？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/-bva0Cako549qUqK5BIHkw">大学毕业是去大城市好还是回小城市好？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/lr6qlbB6loJ1DMbdV-_Avw">大二那年，我发现学生思维在职场行不通</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/UZyUclzkKIRhUM-8vioxvg">B站崩了，如何防止类似事故的出现？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/QW8a0ULgSo9uNyswril3Fg">每日灵魂拷问：“工作&#x3D;有钱&#x3D;快乐？”</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/zv3hhsjCSrzrFIe3rEZRYQ">比渣男pua更可怕的，是职场pua</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/MFtq7jEqnhfi-3OnTCuY5g">你热爱编程么？我先来：爱过</a></p>
</li>
</ul>
</li>
<li><p>程序人生系列</p>
<ul>
<li><p>成长经历</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/IM6ksfs4bWnOljB0EbsyxQ">嗯，读书就是我们唯一的出路</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/FJC2uEg9o56dqCPijgizFA">大学个人经历 | 如诗的青春</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/417im7XPERSMzGiHnNaiEQ">人生如水，由溪入海</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/66ZDj60KPEfohHg0g8Cggw">2019年个人经历</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/_HCBjYI9bcNy-zBHu58l7g">昂，我24岁了</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/aK6ASJO6G1q4mY3TUShCqQ">24岁我全款买了人生的第一辆车</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/H88XxwF3GCwc_cL4rGKu8g">2020个人经历</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/matyTWXuZ_qs9hGfAl9_NQ">我的青春结束了</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/oY3CxB2Dk82mBs4W77Zrcw">鲜衣怒马少年时，一日看尽长安花</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/WPY13Px6OeGssBkZVOL7Aw">敖丙离职了</a></p>
</li>
</ul>
</li>
<li><p>职场经历</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/JB6CT4SfOg8e9lhkiWwZNg">蘑菇街裁员说明 海阔凭鱼跃，天高任鸟飞</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/LjFfDwT62usphaock5pU8Q">入职一周年，我眼中的蘑菇街</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/_zK6ovSynkcmZ-iwzFHHMA">在美女如云的蘑菇街工作是怎样的体验？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/0WurpFJlZQtjjdleGPK0Cw">【互联网寒冬】经历裁员，拿20W被迫去大厂</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/dzxUtvI1m9uO1OpkkcDq0g">再见了，蘑菇街最帅的男人</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/br7Ak88kA5GkZ26hxwKUiw">敖丙用20行代码拿了比赛冠军</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/fkByjmdaqdw0TELDzdm5mQ">敖丙我参加了蘑菇街年会，流了一晚上鼻血</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/wlfurOBpxeaf6iy7CtHRlA">时间刺客–敖丙</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/pHcgkDvKfElRIVZGd4Kzng">送别了好友，敖丙跟你聊聊【离职】这个话题</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/7t2-L7ZYAYXrp0CTbYpAUg">再见，三歪</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/MijvXeWtsJOJIzxcpJdRhA">阿里入职培训</a></p>
</li>
</ul>
</li>
<li><p>自媒体经历</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/g6SQ6nqjKrqjlzaGuLHpYA">写作一个月的感受</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/UC6NsEFlNfqMdEkzvHxKRA">我知道互联网不相信眼泪，但是敖丙今天还是没忍住</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/_5tVdE9oFPBUK3Z0gKH26g">50天全网2W粉，感谢坚持！</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/SBnev4kAzsqpA9l-unt4dw">华为现场和百万粉博主线下面基，敖丙c位出道</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/KB7Gkdu4S13mQnlHGAUHfQ">敖丙第一次见到一个亿…</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/LvgziYKKE7lLIxag1v3R1A">敖丙入职腾讯了</a></p>
</li>
</ul>
</li>
<li><p>生活趣事  </p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/mLLbpnI1pVnlUzL7H3EuNQ">敖丙的第一次相亲，还没开始，就已经结束了。</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/bM7xtVDwAA82d42UrhEx2g">我第一次相亲就被大三岁的姐姐看上</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/sM3hJe5z3_pCH-8GWs0iOA">敖丙参加了一场温州游资的婚礼</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/1jQnRCXdrUQzZInQmzABvA">敖丙枯燥无味的假期，一直在吃狗粮</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/5C4UjGtHoZVu8uI4yP5wRg">请照顾好自己，周末病魔差点一套带走我。</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/s9HPYYi9VfYMt7UGCTqWVw">曾经我们并肩作战，敬未来一杯，敬资本一杯</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/yB9s771gDz6oMKZsUnJuyg">敖丙我写了一个新手都写不出的低级bug，被骂惨了。</a></p>
</li>
</ul>
</li>
<li><p>读者经历</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/irjvJkIRknxasM0D1v3FOw">从网瘾少年逆袭拿到微软 、字节等offer（上）</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/CTpY8oxxjdvleTotjrpAsA">从网瘾少年逆袭拿到微软 、字节等offer（下）</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/oSKKUOG-JO0PLMun13gi0A">我新浪学妹的北京日常</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/cMZN_T7Ar1skWyyx8iHI5g">朋友被裁员之后的工行、华为外包工作经历分享</a>    </p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Bg0Qejvxd6nQMuG4gXFzLw">和华为的一位学长聊了很久，优秀的人果然都有过人之处</a></p>
</li>
</ul>
</li>
<li><p>家人</p>
<ul>
<li><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/yGcgvp9s18CERp9nx2iigw">我的母亲</a></li>
</ul>
</li>
<li><p>过年特辑</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/MXSWBVQyVD4OW0tjy5UO8Q">贵州打工仔回家过年，遭遇流感，被隔离，偶遇读者，偶遇直播同行…</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/pQrepZAbgP59gmj42Z1kdA">敖丙回家过年，外婆说没带女朋友别回来了？喝了老爸89年的酒，当场反目。</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/VECNJbVV0Bz8PKlG8pYwVw">书房翻杂物，看到初恋的信件，看到奖牌，看到梅西、力宏，帅丙的眼角又湿了…..</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/8blBtbBLJtVvpnrJ7tmh_g">疫情之下，从一座空城，到另一座空城，贵州小伙带你看不一样的杭州</a></p>
</li>
</ul>
</li>
</ul>
</li>
<li><p>科普</p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/WszidUcoV_cYeDf26EkAcA">你知道程序是怎么处理时区问题的么？</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/BMttkFZsKu0qmxSz-MqAlw">聊一聊理财</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/13ls90r8hWbzW4J1kuVzrQ">Swagger 官方 Starter 配上这个增强方案是真的香！</a></p>
</li>
</ul>
</li>
<li><p>人物&amp;杂谈  </p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/FUbxHFAQeUgV59Zhp7Fe0w">罗永浩</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Udd3B4L-94hH9ggEtOcJAw">俞敏洪</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/RasTw7_XpxZoDP5k2bpz0w">远方是门票机酒，前方是山川大海</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/lt_RxXC_OAl7aezCkmSyyg">门户前浪退市，从此只做“新浪”</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/a4CzPbx5Bim6mFzzqq0vrA">“小破站”二次上市 - ( ゜- ゜)つロ 乾杯~</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/s2xcFud-8IJq7MpgO1QSHA">我们在努力成为恒星</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/bTWTgiQInITy9Dv5BJ-Dgg">苏宁张近东：三十年沉沉浮浮，零售史上不为人知的秘密</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/LyWW3tXSNMGY6MNi-HnNOg">陈欧，带货网红鼻祖的起起落落落落落落创业路</a></p>
</li>
</ul>
</li>
<li><p><strong>福利</strong></p>
<ul>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/5QpuDtXAalR-pz59B5t27g">Java&#x2F;后端学习路线</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/QrRVMp6z7HjZA4trsga_cg">Java&#x2F;后端学习路线 2.0优化版本</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/Wod2cGjGkGeB0iLVAqpksQ">整理的书单(附个人喜欢的文学书)</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/7NS452k5hVPW0dUQ-oTi-A">敖丙大学四年自学走来，这些私藏的实用工具&#x2F;学习网站我贡献出来了</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/WYiXHVJ-p1sZAf4jb5gIXg">整理好用的工具集</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/JX72OoiNrZ9R0DTuOOtcoA">通用的学习方法</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://github.com/AobingJava/JavaFamily/blob/master/docs/idea/idea.md">IDEA破解(请勿传播)</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://github.com/AobingJava/JavaFamily/blob/master/docs/idea/%E7%94%B5%E5%AD%90%E4%B9%A6.md">电子书(请勿传播)</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://github.com/AobingJava/JavaFamily/blob/master/docs/idea/%E8%B5%84%E6%96%99.md">面试资料(持续更新)</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://github.com/AobingJava/JavaFamily/blob/master/docs/idea/%E8%B5%84%E6%96%99.md">简历模板(欢迎补充)</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://github.com/AobingJava/JavaFamily/blob/master/docs/idea/%E8%B5%84%E6%96%99.md">概要设计模板</a></p>
</li>
<li><p><a target="_blank" rel="noopener" href="https://mp.weixin.qq.com/s/2_hM5Z9IMLIRQ2QEPTABcQ">5分钟白嫖敖丙常用的免费效率软件&#x2F;工具！</a></p>
</li>
</ul>
</li>
</ul>
<p>所有文章pdf版本 : 链接:<a target="_blank" rel="noopener" href="https://pan.baidu.com/s/1PKO0LDspwJPNqT6qwqNorQ">https://pan.baidu.com/s/1PKO0LDspwJPNqT6qwqNorQ</a>  密码:f654</p>
<p>技术交流群：添加我微信  Aobingcool  备注【加群】即可</p>
<p>投稿：个人精力有限欢迎大家投稿 投稿可获得500-3000元不等的稿酬 点击 <a target="_blank" rel="noopener" href="http://mp.weixin.qq.com/s?__biz=MzAwNDA2OTM1Ng==&mid=305658311&idx=1&sn=4a38e5bbb8845a245452f8ffad9fe4b3&chksm=0cf2db443b855252577ac26a16bbf477942401f7e832bd075866d82a0fa25a02ab5b1534cb72#rd">【查看详情】</a></p>
<h1 id="祝福"><a href="#祝福" class="headerlink" title="祝福"></a>祝福</h1><p>希望大家都能找到心仪的工作，学习是一条时而郁郁寡欢，时而开环大笑的路，加油。</p>
<p>如果你通过努力成功进入到了心仪的公司，一定不要懈怠放松，职场成长和新技术学习一样，不进则退。</p>
<p>敖丙在工作中发现我身边的人真的就是实力越强的越努力，<strong>最高级的自律，享受孤独</strong>。</p>
<h1 id="知识星球（近期会重启在思考内容）"><a href="#知识星球（近期会重启在思考内容）" class="headerlink" title="知识星球（近期会重启在思考内容）"></a>知识星球（近期会重启在思考内容）</h1><p><strong><a target="_blank" rel="noopener" href="https://github.com/AobingJava/JavaFamily/blob/master/docs/idea/%E7%9F%A5%E8%AF%86%E6%98%9F%E7%90%83.md"><strong>【点击了解详细 领取减32的优惠券】</strong></a></strong></p>
<p>我会在里面分享个人经历、分享经验、理财，职业规划，答疑等都会涉及，嘉宾分别是公众号大V、大厂架构师、阿里、美团、拼多多的技术专家，都是我朋友，希望能跟你共事（已经有读者去了阿里，也有一个来了蘑菇跟我做同事了，找机会我会分享出来）。</p>
<h1 id="项目经验-amp-服务器"><a href="#项目经验-amp-服务器" class="headerlink" title="项目经验&amp;服务器"></a>项目经验&amp;服务器</h1><p>大家都说简历没项目写，我就帮大家找了一个项目，还附赠<a target="_blank" rel="noopener" href="https://github.com/AobingJava/JavaFamily/blob/master/docs/coderLife/%E6%95%99%E4%BD%A0%E5%9C%A8%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%90%AD%E5%BB%BA%E4%B8%AA%E4%BA%BA%E9%9D%A2%E8%AF%95%E9%A1%B9%E7%9B%AE.md"><strong>【搭建教程】</strong></a>。</p>
<p align="center">
  <a target="_blank" rel="noopener" href="https://activity.huaweicloud.com/1111_promotion/index.html?fromacct=662497fd-8600-411b-9afd-ee2e218560a5&utm_source=V1g3MDY4NTY=&utm_medium=cps&utm_campaign=201905">
  <img src="https://tva1.sinaimg.cn/large/00831rSTly1gdfi1pc65nj306q02aglm.jpg">
  </a>
</p>

<p>云耀云服务器，88元一年起</p>
<p>华为云  <a target="_blank" rel="noopener" href="https://activity.huaweicloud.com/1111_promotion/index.html?fromacct=662497fd-8600-411b-9afd-ee2e218560a5&utm_source=V1g3MDY4NTY=&utm_medium=cps&utm_campaign=201905"><strong>【点击购买】</strong></a></p>
<p align="center">
  <a target="_blank" rel="noopener" href="https://dashi.aliyun.com/site/aobing/ali">
  <img src="https://tva1.sinaimg.cn/large/00831rSTly1gdfi3cmfhgj307e02k0sn.jpg">
  </a>
</p>

<p>活动折扣价：89&#x2F;年，223&#x2F;3年，比学生9.9每月还便宜(只阿里云新用户可用)</p>
<p>阿里云 <a target="_blank" rel="noopener" href="https://dashi.aliyun.com/site/aobing/ali"><strong>【点击购买】</strong></a></p>
<p align="center">
  <a target="_blank" rel="noopener" href="https://partners.cloud.tencent.com/invitation/1000144091425ee1dbcbc6093/100016776501">
  <img src="https://tva1.sinaimg.cn/large/0081Kckwly1gl0drrm2cjj308c02zglg.jpg"/>
  </a>
</p>

<p>腾讯云 <a target="_blank" rel="noopener" href="https://partners.cloud.tencent.com/invitation/1000144091425ee1dbcbc6093/100016776501"><strong>【点击购买】</strong></a></p>
<h1 id="岗位内推"><a href="#岗位内推" class="headerlink" title="岗位内推"></a>岗位内推</h1><p>找所有职位比较出色的朋友，觉得合适的给我简历我可以内推，阿里、美团、蘑菇街、拼多多、字节跳动、腾讯等都可以.</p>
<p>阿里巴巴&#x2F;腾讯&#x2F;字节&#x2F;快手其他岗位 : <a href="mailto:&#97;&#x6f;&#95;&#98;&#x69;&#110;&#103;&#x40;&#102;&#111;&#120;&#109;&#x61;&#105;&#x6c;&#46;&#x63;&#x6f;&#x6d;">&#97;&#x6f;&#95;&#98;&#x69;&#110;&#103;&#x40;&#102;&#111;&#120;&#109;&#x61;&#105;&#x6c;&#46;&#x63;&#x6f;&#x6d;</a></p>
<h2 id="社招近期机会"><a href="#社招近期机会" class="headerlink" title="社招近期机会"></a>社招近期机会</h2><h3 id="阿里校招学长内推"><a href="#阿里校招学长内推" class="headerlink" title="阿里校招学长内推"></a>阿里校招学长内推</h3><p><a href="mailto:&#97;&#x6f;&#95;&#98;&#105;&#x6e;&#103;&#64;&#102;&#111;&#x78;&#x6d;&#x61;&#x69;&#108;&#x2e;&#99;&#x6f;&#x6d;">&#97;&#x6f;&#95;&#98;&#105;&#x6e;&#103;&#64;&#102;&#111;&#x78;&#x6d;&#x61;&#x69;&#108;&#x2e;&#99;&#x6f;&#x6d;</a></p>
<h3 id="涂鸦智慧商业"><a href="#涂鸦智慧商业" class="headerlink" title="涂鸦智慧商业:"></a>涂鸦智慧商业:</h3><p>前端小伙伴欢迎尝试 ～</p>
<h4 id="关于部门："><a href="#关于部门：" class="headerlink" title="关于部门："></a>关于部门：</h4><p>我们大部门这边主要负责 SaaS 业务平台前端的开发，包括中后台，跨端以及工程化等方向。如果对其它团队感兴趣，也可以帮推。</p>
<h4 id="进来可以做的事情："><a href="#进来可以做的事情：" class="headerlink" title="进来可以做的事情："></a>进来可以做的事情：</h4><p>大部门这边有好些方向，比方说你擅长或者视频、WebRTC 相关技术，那可以来我们安防。或者你有其它喜欢的方向，也可以聊聊看。 有很多事来了可以一起做，比方说最近我们部门在做 React to Web Components 相关的技术、业务场景落地。</p>
<h4 id="关于技术栈："><a href="#关于技术栈：" class="headerlink" title="关于技术栈："></a>关于技术栈：</h4><p>大部门这边主要是以 React 为主，如果你是 Vue 经验的，同样欢迎来聊聊。只是框架层面不同，这个不是重点。</p>
<h4 id="关于面试："><a href="#关于面试：" class="headerlink" title="关于面试："></a>关于面试：</h4><p>之前遇到过 Vue 经验的小伙伴问我，说会不会问很多 React 的问题。放心，不会的。像比如社招的话，还是会更加注重项目经验的。然后就是，简历到我这里，不会直接就推进系统，会先跟你沟通一下哈，觉得 ok 我们再走流程。最后，流程会全程跟进，可加微信直接找我查进度。<br>邮箱: <a href="mailto:&#x7a;&#111;&#x6e;&#103;&#x77;&#x65;&#105;&#46;&#104;&#x75;&#64;&#x74;&#117;&#121;&#97;&#46;&#99;&#x6f;&#x6d;">&#x7a;&#111;&#x6e;&#103;&#x77;&#x65;&#105;&#46;&#104;&#x75;&#64;&#x74;&#117;&#121;&#97;&#46;&#99;&#x6f;&#x6d;</a> 微信联系: 15586548537 (昵称是接水怪，不要加错了哦~)</p>
<h3 id="支付宝网商银行"><a href="#支付宝网商银行" class="headerlink" title="支付宝网商银行:"></a>支付宝网商银行:</h3><p>Java工作两年及以上的同学欢迎尝试～</p>
<h4 id="关于部门：-1"><a href="#关于部门：-1" class="headerlink" title="关于部门："></a>关于部门：</h4><p>网商银行不做过多介绍了，网上很多相关资料，跟腾讯微众对标，每年绩效都很好。</p>
<p>我这边是信贷决策部门，绝对的核心，部门直招，如果对蚂蚁其他团队感兴趣也可以帮推，但没有本部门那么方便跟进。</p>
<p>进来可以做的事情：</p>
<p>可以保证的是一定不是单纯搬砖，本部门在整个网商都是底层核心系统，无论是流量还是数据量都是极大的技术挑战。</p>
<h4 id="关于职级："><a href="#关于职级：" class="headerlink" title="关于职级："></a>关于职级：</h4><p>p6-p8都有hc，真实hc不刷kpi，目前网商银行业务扩张，目标人数*2，所以放出了很多hc，是进蚂蚁的好机会。</p>
<h4 id="关于面试：-1"><a href="#关于面试：-1" class="headerlink" title="关于面试："></a>关于面试：</h4><p>很多同学可能很担心投了没有面好会对以后有影响，或者投了之后面下来发现不合适，这里说明一下，我们不会马上把你简历录系统，会先进行评估并且跟你电话沟通，双方都觉得合适才会开始面试，不会一来就传简历，而且面试时间和进度我可以随时帮跟进，不存在放鸽子这种情况。</p>
<p>邮箱: <a href="mailto:&#x6c;&#119;&#50;&#x36;&#x33;&#x38;&#x38;&#56;&#64;&#97;&#x6c;&#x69;&#x62;&#x61;&#x62;&#97;&#45;&#x69;&#110;&#99;&#x2e;&#99;&#111;&#x6d;">&#x6c;&#119;&#50;&#x36;&#x33;&#x38;&#x38;&#56;&#64;&#97;&#x6c;&#x69;&#x62;&#x61;&#x62;&#97;&#45;&#x69;&#110;&#99;&#x2e;&#99;&#111;&#x6d;</a>  微信联系: lwwei2222</p>
<h3 id="天猫国际"><a href="#天猫国际" class="headerlink" title="天猫国际:"></a>天猫国际:</h3><p>天猫国际是中国消费升级的第一跨境平台，是阿里经济体5年2000亿美金进口承诺的担纲。</p>
<p>2019年天猫国际技术部和考拉合并成立了阿里巴巴大进口技术部，是阿里巴巴国际化战略的核心技术部门。</p>
<p>我们致力于进口业务的技术突破和创新，助力中国的消费者实现“买遍全球”的需求。希望你加入我们，同我们一道引领电子商务、电子支付、供应链等全球化方向，建立一个让全球消费者喜爱，让全球商家受益的国际化技术体系。</p>
<p>1.你将致力于进口电商相关的推荐、搜索、用户增长、供应链规划等算法的设计和研发</p>
<p>2.你将致力于应用人工智能的最新技术，提高自动化和智能化程度，大幅提升业务效率</p>
<p>3.你将有机会深入到最前沿的大数据电商系统的设计和研发。近期成立的新BU，发展前景广阔，提供你足够施展才能的舞台。</p>
<p>现招聘 java研发实习生、前端实习生、算法、数据分析和测试实习生。</p>
<p>有意请发简历到 <a href="mailto:&#x6a;&#x6f;&#115;&#x68;&#x75;&#x61;&#46;&#x6c;&#105;&#x66;&#102;&#64;&#97;&#x6c;&#x69;&#98;&#97;&#x62;&#97;&#45;&#x69;&#x6e;&#x63;&#x2e;&#99;&#111;&#x6d;">&#x6a;&#x6f;&#115;&#x68;&#x75;&#x61;&#46;&#x6c;&#105;&#x66;&#102;&#64;&#97;&#x6c;&#x69;&#98;&#97;&#x62;&#97;&#45;&#x69;&#x6e;&#x63;&#x2e;&#99;&#111;&#x6d;</a></p>
<p>要求：2020年11-2021年10本科及以上毕业生，理工科相关专业优先，有论文发表或者程序设计大赛、acm等算法比赛获奖的优先。</p>
<h3 id="阿里国际化电商基础链路团队"><a href="#阿里国际化电商基础链路团队" class="headerlink" title="阿里国际化电商基础链路团队:"></a>阿里国际化电商基础链路团队:</h3><ul>
<li><p>Vision：AE基础链路的Vision是打造全球化、灵动性的基础链路团队，构建一个完整的电商基础领域闭环。在这里你可以领略到完整的国际化电商体系的进化和发展，在整个电商基础领域的生命周期里可以任意选择发挥你的所长。只要你敢想，我们就敢一起干；</p>
</li>
<li><p>领域：基于Siruis，Poloris中台架构体系，负责AE核心的交易、营销优惠、支付&amp;结算、会员店铺、钱包等基础域国际化小前台体系建设，并构建差异化的国家站的整体业务解决方案；</p>
</li>
<li><p>创新：基于AE跨境、本地化的多业务形态，小前台结合国际化中台的业务和技术策略，孵化创新业务与解决方案，如海外钱包解决方案、数字商品解决方案，助力海外业务多元化。联合算法赋能基础链路，创造更多结合算法能力的场景，如营销优惠，通过技术促进业务，更好的降成本促增收；</p>
</li>
<li><p>挑战：我们的基础领域业务遍布全世界各个角落，每个国家都会带给我们不一样的挑战和机遇，在这里我们可以重新定义电商基础领域，实现基础链路领域国家差异化；</p>
</li>
</ul>
<h4 id="岗位要求："><a href="#岗位要求：" class="headerlink" title="岗位要求："></a>岗位要求：</h4><ul>
<li><p>3 年及以上使用 JAVA 开发的经验，JAVA 基础扎实，理解io、多线程、集合等基础框架，了解 JVM 原理；</p>
</li>
<li><p>熟悉WEB开发，熟悉Servlet，MVC框架，具有扎实的数据结构与算法知识；</p>
</li>
<li><p>对用过的开源框架，能了解到它的原理和机制；对 Spring、mybatis 等开源框架熟悉；</p>
</li>
<li><p>熟悉分布式系统的设计和应用，熟悉分布式、缓存、消息等机制；能对分布式常用技术进行合理应用，解决问题；</p>
</li>
<li><p>熟悉 Linux 操作系统和大型数据库（RDBMS, NoSQL, NewSQL）；对 sql 优化有丰富的经验；</p>
</li>
<li><p>工作积极主动、很好的沟通能力、学习能力，适应能力好；能接受全栈任务，具备耐心和细心的品质。</p>
</li>
<li><p>英语流利为加分项</p>
</li>
</ul>
<p>有意请发简历到 <a href="mailto:&#102;&#105;&#x67;&#x68;&#116;&#x69;&#110;&#x67;&#x63;&#x72;&#97;&#112;&#x40;&#103;&#109;&#97;&#105;&#x6c;&#46;&#x63;&#111;&#109;">&#102;&#105;&#x67;&#x68;&#116;&#x69;&#110;&#x67;&#x63;&#x72;&#97;&#112;&#x40;&#103;&#109;&#97;&#105;&#x6c;&#46;&#x63;&#111;&#109;</a></p>
<h1 id="鸣谢列表"><a href="#鸣谢列表" class="headerlink" title="鸣谢列表"></a>鸣谢列表</h1><p>以下排名不分先后!</p>
<a target="_blank" rel="noopener" href="https://github.com/simpleTo">
    <img src="https://avatars3.githubusercontent.com/u/24934495?s=400&v=4" width="45px">
</a>
<a target="_blank" rel="noopener" href="https://github.com/programes">  
    <img src="敖丙的文章链接/6359325.png" width="45px">
</a>
<a target="_blank" rel="noopener" href="https://github.com/bertChen812">  
    <img src="https://avatars3.githubusercontent.com/u/37893362?s=400&v=4" width="45px">
</a>
<a target="_blank" rel="noopener" href="https://github.com/cyberwave">
    <img src="https://avatars2.githubusercontent.com/u/7488935?s=400&v=4" width="45px">
</a>
<a target="_blank" rel="noopener" href="https://github.com/Gene1994">
    <img src="https://avatars3.githubusercontent.com/u/24930369?s=460&v=4" width="45px">
</a>
<a target="_blank" rel="noopener" href="https://github.com/bertChen812">
    <img src="https://avatars0.githubusercontent.com/u/12581996?s=460&v=4" width="45px">
</a>
<a target="_blank" rel="noopener" href="https://github.com/illusorycloud">
    <img src="https://avatars3.githubusercontent.com/u/31980412?s=460&v=4" width="45px">
</a>
<a target="_blank" rel="noopener" href="https://github.com/LiWenGu">
    <img src="https://avatars0.githubusercontent.com/u/15909210?s=460&v=4" width="45px">
</a>
<a target="_blank" rel="noopener" href="https://github.com/kinglaw1204">
    <img src="https://avatars1.githubusercontent.com/u/20039931?s=460&v=4" width="45px">
</a>
<a target="_blank" rel="noopener" href="https://github.com/jun1st">
    <img src="https://avatars2.githubusercontent.com/u/14312378?s=460&v=4" width="45px">
</a>"
<a target="_blank" rel="noopener" href="https://github.com/fantasygg">  
    <img src="https://avatars3.githubusercontent.com/u/13445354?s=460&v=4" width="45px">
</a>
<a target="_blank" rel="noopener" href="https://github.com/debugjoker">  
    <img src="https://avatars3.githubusercontent.com/u/26218005?s=460&v=4" width="45px">
</a>
<a target="_blank" rel="noopener" href="https://github.com/zhyank">  
    <img src="https://avatars0.githubusercontent.com/u/17696240?s=460&v=4" width="45px">
</a>
<a target="_blank" rel="noopener" href="https://github.com/Goose9527">  
    <img src="https://avatars2.githubusercontent.com/u/43314997?s=460&v=4" width="45px">
</a>
<a target="_blank" rel="noopener" href="https://github.com/yuechuanx">  
    <img src="https://avatars3.githubusercontent.com/u/19339293?s=460&v=4" width="45px">
</a>
<a target="_blank" rel="noopener" href="https://github.com/cnLGMing">  
    <img src="https://avatars2.githubusercontent.com/u/15910705?s=460&v=4" width="45px">
</a>



<h1 id="赞助商"><a href="#赞助商" class="headerlink" title="赞助商"></a>赞助商</h1><p>以下排名不分先后!<br><img src="/%E6%95%96%E4%B8%99%E7%9A%84%E6%96%87%E7%AB%A0%E9%93%BE%E6%8E%A5/007S8ZIlly1ge4xvdlh0wj30zk0d1q6d.jpg"></p>
<p>  <a name="微信"></a>  <a name="公众号"></a></p>
<p> <img src="/%E6%95%96%E4%B8%99%E7%9A%84%E6%96%87%E7%AB%A0%E9%93%BE%E6%8E%A5/00831rSTly1gck2pplt8ej315u0u0k1b.jpg"></p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/30/Reactor%E5%92%8CProactor/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/30/Reactor%E5%92%8CProactor/" class="post-title-link" itemprop="url">Reactor模式和Proactor模式</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-30 09:19:26" itemprop="dateCreated datePublished" datetime="2021-11-30T09:19:26+08:00">2021-11-30</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-09-23 01:40:24" itemprop="dateModified" datetime="2022-09-23T01:40:24+08:00">2022-09-23</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E8%AE%BE%E8%AE%A1%E6%A8%A1%E5%BC%8F/" itemprop="url" rel="index"><span itemprop="name">设计模式</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <p>Reactor 模式也叫 Dispatcher 模式，我觉得这个名字更贴合该模式的含义，即 <strong>I&#x2F;O</strong> 多路复⽤监<br>听事件，收到事件后，根据事件类型分配（<strong>Dispatch</strong>）给某个进程&#x2F;线程。</p>
<p>Reactor 模式主要由 <strong>Reactor</strong> 和<strong>处理资源池</strong>这两个核⼼部分组成，它俩负责的事情如下：</p>
<ol>
<li><strong>Reactor 负责监听和分发事件，事件类型包含连接事件、读写事件</strong>；</li>
<li><strong>处理资源池负责处理事件，如 read -&gt; 业务逻辑 -&gt; send</strong>；</li>
</ol>
<p>Reactor 模式是灵活多变的，可以应对不同的业务场景，灵活在于：</p>
<ol>
<li>Reactor 的数量可以只有⼀个，也可以有多个；</li>
<li>处理资源池可以是单个进程 &#x2F; 线程，也可以是多个进程 &#x2F;线程；</li>
</ol>
<p>将上⾯的两个因素排列组设⼀下，理论上就可以有 4 种⽅案选择：</p>
<ol>
<li>单 Reactor 单进程 &#x2F; 线程；</li>
<li>单 Reactor 多进程 &#x2F; 线程；</li>
<li>多 Reactor 单进程 &#x2F; 线程；</li>
<li>多 Reactor 多进程 &#x2F; 线程；</li>
</ol>
<p>其中，「多 Reactor 单进程 &#x2F; 线程」实现⽅案相⽐「单 Reactor 单进程 &#x2F; 线程」⽅案，不仅复杂⽽且也没有性能优势，因此实际中并没有应⽤。</p>
<p>剩下的 3 个⽅案都是⽐较经典的，且都有应⽤在实际的项⽬中：<br>单 Reactor 单进程 &#x2F; 线程；<br>单 Reactor 多线程 &#x2F; 进程；<br>多 Reactor 多进程 &#x2F; 线程；( Netty 和 Memcache 都采⽤了「多 Reactor 多线程、「多 Reactor 多进程」⽅案的开源软件是 Nginx)</p>
<p>⽅案具体使⽤进程还是线程，要看使⽤的编程语⾔以及平台有关：</p>
<p>Java 语⾔⼀般使⽤线程，⽐如 Netty;</p>
<p>C 语⾔使⽤进程和线程都可以，例如 Nginx 使⽤的是进程，Memcache 使⽤的是线程。</p>
<p>接下来，分别介绍这三个经典的 Reactor ⽅案。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/30/lua/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/30/lua/" class="post-title-link" itemprop="url">Lua脚本以及在redis中使用lua</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>
      

      <time title="创建时间：2021-11-30 08:13:10 / 修改时间：09:11:04" itemprop="dateCreated datePublished" datetime="2021-11-30T08:13:10+08:00">2021-11-30</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Redis/" itemprop="url" rel="index"><span itemprop="name">Redis</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="什么是lua"><a href="#什么是lua" class="headerlink" title="什么是lua"></a>什么是lua</h1><h2 id="Mac安装-lua"><a href="#Mac安装-lua" class="headerlink" title="Mac安装 lua"></a>Mac安装 lua</h2><p><strong>安装</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">brew search lua</span><br><span class="line">brew install lua</span><br></pre></td></tr></table></figure>

<p><strong>进入shell</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">lua</span><br></pre></td></tr></table></figure>

<p><strong>运行脚本文件</strong></p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">chmod +x test_lua.lua</span><br><span class="line">/test_lua.lua</span><br></pre></td></tr></table></figure>


<h1 id="基本语法"><a href="#基本语法" class="headerlink" title="基本语法"></a>基本语法</h1><h2 id="注释"><a href="#注释" class="headerlink" title="注释"></a>注释</h2><p><strong>行注释</strong></p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 这是注释</span></span><br></pre></td></tr></table></figure>


<p><strong>段注释</strong></p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">--[[</span></span><br><span class="line"><span class="comment">这是块注释</span></span><br><span class="line"><span class="comment">这是块注释</span></span><br><span class="line"><span class="comment">--]]</span></span><br></pre></td></tr></table></figure>





<h2 id="变量"><a href="#变量" class="headerlink" title="变量"></a>变量</h2><h2 id="全局变量"><a href="#全局变量" class="headerlink" title="全局变量"></a>全局变量</h2><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">a = <span class="number">1</span></span><br><span class="line"><span class="built_in">print</span>(b) <span class="comment">-- 无初始值为 nil</span></span><br></pre></td></tr></table></figure>


<h3 id="局部变量"><a href="#局部变量" class="headerlink" title="局部变量"></a>局部变量</h3><p>在redis脚本中不能使用全局变量，只能使用局部变量（其实是为了防止脚本之间相互影响）</p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> a = <span class="number">1</span></span><br><span class="line"><span class="keyword">local</span> b</span><br><span class="line"><span class="keyword">local</span> c, d</span><br><span class="line"><span class="keyword">local</span> e, f, g = <span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span></span><br></pre></td></tr></table></figure>


<h2 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h2><h3 id="空"><a href="#空" class="headerlink" title="空"></a>空</h3><p>没有赋值的变量或者表的字段即为nil，空。</p>
<h3 id="布尔"><a href="#布尔" class="headerlink" title="布尔"></a>布尔</h3><p>只有true，false。</p>
<h3 id="数字"><a href="#数字" class="headerlink" title="数字"></a>数字</h3><p>包括整数、浮点数，1、2.0、3.5e10</p>
<h3 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h3><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">local</span> s1 = <span class="string">&#x27;a&#x27;</span></span><br><span class="line"><span class="keyword">local</span> s2 = <span class="string">&quot;abc&quot;</span></span><br></pre></td></tr></table></figure>



<h3 id="表"><a href="#表" class="headerlink" title="表"></a>表</h3><p>lua中唯一的数据结构，既可以当数组，也可以当字典。</p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">a = &#123;&#125;</span><br><span class="line">b = &#123;<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>&#125;</span><br><span class="line">c = &#123;<span class="string">&#x27;s1&#x27;</span>, <span class="string">&#x27;s2&#x27;</span>, <span class="string">&#x27;s3&#x27;</span>&#125;</span><br><span class="line">d = &#123;<span class="number">1</span>, <span class="string">&#x27;s&#x27;</span>, <span class="string">&#x27;a&#x27;</span>&#125;</span><br><span class="line">c[<span class="number">1</span>] <span class="comment">-- &#x27;s1&#x27;</span></span><br><span class="line">d[<span class="number">1</span>] = d[<span class="number">1</span>] + <span class="number">1</span></span><br></pre></td></tr></table></figure>


<p><strong>注意：Lua规定数组的索引从 1 开始的，而不是 0</strong></p>
<h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="params">(函数列表)</span></span></span><br><span class="line">  函数题</span><br><span class="line"><span class="keyword">end</span> </span><br></pre></td></tr></table></figure>


<p>例子1</p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">m</span><span class="params">(a,b)</span></span></span><br><span class="line">  <span class="keyword">return</span> a*b</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure>

<p>递归</p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fun</span><span class="params">(n)</span></span></span><br><span class="line">  <span class="keyword">if</span> n &lt; <span class="number">2</span> <span class="keyword">then</span> <span class="keyword">return</span> <span class="number">1</span> <span class="keyword">end</span></span><br><span class="line">  <span class="keyword">return</span> fun(n - <span class="number">2</span>) + fun(n - <span class="number">1</span>)</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure>



<h2 id="控制语句"><a href="#控制语句" class="headerlink" title="控制语句"></a>控制语句</h2><h3 id="if"><a href="#if" class="headerlink" title="if"></a>if</h3><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(<span class="number">5</span>&gt;<span class="number">10</span>) <span class="keyword">then</span></span><br><span class="line">   <span class="built_in">print</span>(<span class="string">&#x27;10&#x27;</span>);</span><br><span class="line"><span class="keyword">elseif</span> <span class="number">15</span>&gt;<span class="number">10</span> <span class="keyword">then</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;15&#x27;</span>);</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">    <span class="built_in">print</span>(<span class="string">&#x27;0&#x27;</span>);</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure>



<h3 id="while-循环"><a href="#while-循环" class="headerlink" title="while 循环"></a>while 循环</h3><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span> 条件表达式 <span class="keyword">do</span></span><br><span class="line">  语句块</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure>



<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">sum = <span class="number">0</span></span><br><span class="line">num = <span class="number">1</span></span><br><span class="line"><span class="comment">-- 注意 do 和 end</span></span><br><span class="line"><span class="keyword">while</span> num &lt;= <span class="number">100</span> <span class="keyword">do</span></span><br><span class="line">    sum = sum + num</span><br><span class="line">    num = num + <span class="number">1</span></span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&quot;sum =&quot;</span>,sum)</span><br></pre></td></tr></table></figure>



<h3 id="for-循环"><a href="#for-循环" class="headerlink" title="for 循环"></a>for 循环</h3><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> 变量 = 初值, 终值, 步长 <span class="keyword">do</span></span><br><span class="line">  语句块</span><br><span class="line"><span class="keyword">end</span>  </span><br></pre></td></tr></table></figure>

<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">sum = <span class="number">0</span></span><br><span class="line"><span class="comment">-- 默认步长是1</span></span><br><span class="line"><span class="keyword">for</span> i = <span class="number">1</span>, <span class="number">100</span> <span class="keyword">do</span></span><br><span class="line">    sum = sum + i</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure>



<p>另一个格式</p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> var1, var2, ..., varn <span class="keyword">in</span> 迭代器 <span class="keyword">do</span></span><br><span class="line">  语句块</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure>





<p><strong>表的遍历</strong></p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line">a = &#123;<span class="number">1</span>, <span class="number">3</span>, <span class="number">5</span>, <span class="number">7</span>&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- #获取表的长度</span></span><br><span class="line"><span class="keyword">for</span> i = <span class="number">1</span>, #a <span class="keyword">do</span></span><br><span class="line">  <span class="built_in">print</span>(i)</span><br><span class="line">  <span class="built_in">print</span>(a[i])</span><br><span class="line"><span class="keyword">end</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 使用 ipairs</span></span><br><span class="line"><span class="keyword">for</span> index, value <span class="keyword">in</span> <span class="built_in">ipairs</span>(a) <span class="keyword">do</span></span><br><span class="line">  <span class="built_in">print</span>(index)</span><br><span class="line">  <span class="built_in">print</span>(value)</span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure>



<h1 id="常用库"><a href="#常用库" class="headerlink" title="常用库"></a>常用库</h1><h2 id="String库"><a href="#String库" class="headerlink" title="String库"></a>String库</h2><ol>
<li><p>获取长度</p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">a = <span class="string">&#x27;abc&#x27;</span></span><br><span class="line"><span class="built_in">print</span>(#a) <span class="comment">-- 长度为3</span></span><br><span class="line"><span class="built_in">print</span>(<span class="built_in">string</span>.<span class="built_in">len</span>(<span class="string">&#x27;abc&#x27;</span>))</span><br></pre></td></tr></table></figure>


</li>
<li><p>大小写转换</p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">string</span>.<span class="built_in">lower</span>(s)</span><br><span class="line"><span class="built_in">string</span>.<span class="built_in">upper</span>(s)</span><br></pre></td></tr></table></figure>

</li>
<li><p>子串</p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 闭区间，end可以取到</span></span><br><span class="line"><span class="built_in">string</span>.<span class="built_in">sub</span>(s, start, [,<span class="keyword">end</span>])</span><br></pre></td></tr></table></figure></li>
</ol>
<h2 id="Table-库"><a href="#Table-库" class="headerlink" title="Table 库"></a>Table 库</h2><p>暂略。</p>
<h2 id="Math-库"><a href="#Math-库" class="headerlink" title="Math 库"></a>Math 库</h2><figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">math</span>.<span class="built_in">max</span>(x, ...)</span><br><span class="line"><span class="built_in">math</span>.<span class="built_in">min</span>(x, ...)</span><br><span class="line"><span class="built_in">math</span>.<span class="built_in">abs</span>(x)</span><br></pre></td></tr></table></figure>





<h1 id="Redis和Lua"><a href="#Redis和Lua" class="headerlink" title="Redis和Lua"></a>Redis和Lua</h1><h3 id="lua使用redis"><a href="#lua使用redis" class="headerlink" title="lua使用redis"></a>lua使用redis</h3><p>在lua脚本使用<code>redis.call</code></p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">redis.call(<span class="string">&#x27;set&#x27;</span>,<span class="string">&#x27;key1&#x27;</span>,<span class="string">&#x27;val1&#x27;</span>)</span><br><span class="line"><span class="keyword">local</span> v = redis.call(<span class="string">&#x27;get&#x27;</span>, <span class="string">&#x27;key2&#x27;</span>)</span><br><span class="line"><span class="keyword">return</span> v</span><br></pre></td></tr></table></figure>

<h3 id="redis-cli中使用lua"><a href="#redis-cli中使用lua" class="headerlink" title="redis-cli中使用lua"></a>redis-cli中使用lua</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">EVAL script numkeys key [key ...] arg [arg ...]</span><br></pre></td></tr></table></figure>



<h3 id="KEYS-和-ARGV"><a href="#KEYS-和-ARGV" class="headerlink" title="KEYS 和 ARGV"></a>KEYS 和 ARGV</h3><p>使用 <code>KEYS</code>获取键，使用<code>ARGV</code>获取值。</p>
<blockquote>
<p>注意索引从 1 开始。</p>
</blockquote>
<h3 id="原子性和执行时间"><a href="#原子性和执行时间" class="headerlink" title="原子性和执行时间"></a>原子性和执行时间</h3><p>Redis执行lua脚本是原子的，也就是在一个脚本执行期间不会执行其他命令。</p>
<p>所有其他的命令都必须在脚本执行完成后才能执行。</p>
<p>为了防止一个脚本执行时间太长，Redis提供<code>lua-time-limit</code>限制脚本的最长运行时间（默认是 5 秒），</p>
<p>时间到了，就可以接受其他命令，但这个脚本的执行就无法无确保原子性了，因为它还没有只执行完。</p>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/29/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E9%9D%A2%E8%AF%95%E9%97%AE%E9%A2%98%E6%B1%87%E6%80%BB/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/29/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C%E9%9D%A2%E8%AF%95%E9%97%AE%E9%A2%98%E6%B1%87%E6%80%BB/" class="post-title-link" itemprop="url">计算机网络面试问题汇总</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-29 23:42:04" itemprop="dateCreated datePublished" datetime="2021-11-29T23:42:04+08:00">2021-11-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-09-30 00:30:38" itemprop="dateModified" datetime="2022-09-30T00:30:38+08:00">2022-09-30</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/%E8%AE%A1%E7%AE%97%E6%9C%BA%E7%BD%91%E7%BB%9C/" itemprop="url" rel="index"><span itemprop="name">计算机网络</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="SYN-FLOOD"><a href="#SYN-FLOOD" class="headerlink" title="SYN FLOOD"></a>SYN FLOOD</h1><p>SYN Flood 是种典型的 DoS（拒绝服务）攻击，其目的是通过消耗服务器所有可用资源使服务器无法用于处理合法请求。通过重复发送初始连接请求（SYN）数据包，攻击者能够压倒目标服务器上的所有可用端口，导致目标设备根本不响应合法请求。</p>
<h2 id="SYN攻击的原理"><a href="#SYN攻击的原理" class="headerlink" title="SYN攻击的原理"></a>SYN攻击的原理</h2><p>在 TCP 建立连接的过程中，<strong>因为服务端不确定自己发给客户端的 SYN-ACK 消息或客户端反馈的 ACK 消息是否会丢在半路，所以会给每个待完成的半开连接状态设一个定时器</strong>，如果超过时间还没有收到客户端的 ACK 消息，则重新发送一次 SYN-ACK 消息给客户端，直到重试超过一定次数时才会放弃。</p>
<p><strong>服务端为了维持半开连接状态，需要分配内核资源维护半开连接</strong>。当攻击者伪造海量的虚假 IP 向服务端发送 SYN 包时，就形成了 SYN FLOOD 攻击。攻击者故意不响应 ACK 消息，导致服务端被大量注定不能完成的半开连接占据，直到资源耗尽，停止响应正常的连接请求。</p>
<p><strong>SYN flood攻击目前有两种方法，不过都与服务端没收到<em>ACK</em>有关</strong>。恶意用户可以跳过发送最后的<em>ACK</em>信息；或者在<em>SYN</em>里通过欺骗来源IP地址，这让服务器送<em>SYN-ACK</em>到假造的IP地址，因此永不可能收到<em>ACK</em>。这两个案例服务器会花点时间等待ACK通知，故一个简单的网络拥塞可能是由于没有<em>ACK</em>造成的。</p>
<p>解决方法：</p>
<ol>
<li><p>直接的方法是<strong>提高 TCP 端口容量</strong>，<strong>减少半开连接的资源占用时间</strong>，然而该方法只是稍稍提高了防御能力，算不上解决问题；</p>
</li>
<li><p><strong>部署能够辨别恶意 IP 的路由器</strong>，将伪造 IP 地址的发送方发送的 SYN 消息过滤掉，该方案作用一般不是太大；</p>
</li>
<li><p><strong>SYN Cache</strong>：该方法首先<strong>构造一个全局 Hash Table，用来缓存系统当前所有的半开连接信息</strong>。在 Hash Table 中的每个桶的容量大小是有限制的，<del>当桶满时，会主动丢掉早来的信息</del>。</p>
<p><strong>当服务端收到一个 SYN 消息后，会通过一个映射函数生成一个相应的 Key 值，使得当前半连接信息存入相应的桶中</strong>。<strong>当收到客户端正确的ack报文后，服务端才开始分配传输资源块</strong>，并将相应的半开连接信息从表中删除。<strong>和服务器传输资源相比，维护表的开销要小得多</strong>。</p>
</li>
<li><p><strong>SYN Cookie</strong>：<strong>服务端通过特定的算法将半开连接信息编码成序列号或者时间戳</strong>，<strong>用作服务端给客户端的消息编号，随 SYN-ACK 消息一同返回给连接发起方</strong>，这样<strong>在连接建立完成前服务端不保存任何信息</strong>，<strong>直到发送方发送 ACK 确认报文并且服务端成功验证编码信息后，服务端才开始分配传输资源</strong>。若请求方是攻击者，则不会向服务端会 ACK 消息，由于未成功建立连接，因此服务端并没有花费任何额外的开销。</p>
<p>然而该方案也存在一些缺点，<strong>由于服务端并不保存半开连接状态，因此也就丧失了超时重传的能力</strong>，这在一定程度上降低了正常用户的连接成功率。此外，客户端发送给服务端的确认报文存在传输丢失的可能，当 ACK 确认报文丢失时，服务端和客户端会对连接的成功与否产生歧义，此时就需要上层应用采取相应的策略进行处理了。</p>
</li>
</ol>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/SYN_cookie">SYN cookie wiki</a></p>
<p>发起一个 TCP 连接时，客户端将一个 TCP SYN 包发送给服务器。作为响应，服务器将 TCP SYN + ACK 包返回给客户端。此数据包中有一个<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E4%BC%A0%E8%BE%93%E6%8E%A7%E5%88%B6%E5%8D%8F%E8%AE%AE#.E5.BA.8F.E5.88.97.E5.8F.B7.E5.92.8C.E7.A1.AE.E8.AE.A4">序号</a>（sequence number，TCP头中的第二个32 bit），它被 TCP 用来重新组装数据流。根据 TCP 规范，由端点发送的第一个序号可以是由该端点决定的任何值。SYN Cookies 是根据以下规则构造的初始序号：</p>
<ul>
<li>令 <strong>t</strong> 为一个缓慢递增的时间戳（通常为 <code>time() &gt;&gt; 6 </code>，提供 64 秒的分辨率）；</li>
<li>令 <strong>m</strong> 为服务器会在 SYN 队列条目中存储的<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E6%9C%80%E5%A4%A7%E5%88%86%E6%AE%B5%E5%A4%A7%E5%B0%8F">最大分段大小</a>（maximum segment size，简称为 MSS）；</li>
<li>令 <strong>s</strong> 为一个加密<a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E6%95%A3%E5%88%97%E7%AE%97%E6%B3%95">散列</a>函数对服务器和客户端各自的 IP 地址和端口号以及 <strong>t</strong> 进行运算的结果。返回得到的数值 <strong>s</strong> 必须是一个24位值。</li>
</ul>
<p>初始 TCP 序号，也就是所谓的 <em>SYN cookie</em>，按照如下算法得到：</p>
<ul>
<li>头五位：<strong>t</strong> <a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/%E5%90%8C%E4%BD%99">mod</a> 32；</li>
<li>中三位：<strong>m</strong> 编码后的数值；</li>
<li>末24位：<strong>s</strong> 本身；</li>
</ul>
<p>注：由于 <em>m</em> 必须用 3 位进行编码，服务器在启用了 SYN Cookie 时只能为 <em>m</em> 发送八种不同的数值。</p>
<p>根据 TCP 规范，当客户端发回 TCP ACK 包给服务器以响应服务器的 SYN + ACK 包时，客户端必须使用由服务器发送的初始序号加1作为数据包中的确认号。服务器接着从确认号中减去 1 以便还原向客户端发送的原始 SYN Cookie。</p>
<p>接下来服务器进行以下检查：</p>
<ul>
<li>根据当前的时间以及 <strong>t</strong> 来检查连接是否过期。</li>
<li>重新计算 <strong>s</strong> 来确认这是不是一个有效的 SYN Cookie。</li>
<li>从 3 位编码中解码 <strong>m</strong>，以便之后用来重建 SYN 队列条目。在此之后，连接照常进行。</li>
</ul>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://zh.wikipedia.org/wiki/SYN_flood">SYN flood Wiki</a></p>
<h1 id="从浏览器输入域名到显示出网页的过程"><a href="#从浏览器输入域名到显示出网页的过程" class="headerlink" title="从浏览器输入域名到显示出网页的过程"></a>从浏览器输入域名到显示出网页的过程</h1><ul>
<li>1、输入地址</li>
<li>2、浏览器查找域名的 IP 地址</li>
<li>3、浏览器向 web 服务器发送一个 HTTP 请求</li>
<li>4、服务器的永久重定向响应</li>
<li>6、服务器处理请求</li>
<li>7、服务器返回一个 HTTP 响应</li>
<li>8、浏览器显示 HTML</li>
<li>9、浏览器发送请求获取嵌入在 HTML 中的资源（如图片、音频、视频、CSS、JS等等</li>
</ul>
<blockquote>
<p>浏览器从输入网址到渲染页面主要分为以下几个过程</p>
<ul>
<li>URL 输入</li>
<li><a target="_blank" rel="noopener" href="https://cloud.tencent.com/product/cns?from=10680">DNS 解析</a></li>
<li>建立 TCP 连接</li>
<li>发送 HTTP &#x2F; HTTPS 请求（建立 TLS 连接）</li>
<li><a target="_blank" rel="noopener" href="https://cloud.tencent.com/product/cvm?from=10680">服务器</a>响应请求</li>
<li>浏览器解析渲染页面</li>
<li>HTTP 请求结束，断开 TCP 连接</li>
</ul>
</blockquote>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/lepeCoder/p/15147895.html">https://www.cnblogs.com/lepeCoder/p/15147895.html</a></p>
<p><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/133906695">https://zhuanlan.zhihu.com/p/133906695</a></p>
<h1 id="TCP"><a href="#TCP" class="headerlink" title="TCP"></a>TCP</h1><h2 id="TCP-粘包问题"><a href="#TCP-粘包问题" class="headerlink" title="TCP 粘包问题"></a>TCP 粘包问题</h2><p><strong>为什么会发生TCP粘包和拆包?</strong></p>
<p>① 发送方写入的数据大于套接字缓冲区的大小，此时将发生拆包。</p>
<p>② 发送方写入的数据小于套接字缓冲区大小，由于 TCP 默认使用 Nagle 算法，只有当收到一个确认后，才将分组发送给对端，当发送方收集了多个较小的分组，就会一起发送给对端，这将会发生粘包。</p>
<p>③ 进行 MSS （最大报文长度）大小的 TCP 分段，当 TCP 报文的数据部分大于 MSS 的时候将发生拆包。</p>
<p>④ 发送方发送的数据太快，接收方处理数据的速度赶不上发送端的速度，将发生粘包。</p>
<p><strong>常见解决方法</strong></p>
<p>① <strong>在消息的头部添加消息长度字段</strong>，服务端获取消息头的时候解析消息长度，然后向后读取相应长度的内容。</p>
<p>② 固定消息数据的长度，服务端每次读取既定长度的内容作为一条完整消息，当消息不够长时，空位补上固定字符。但是该方法会浪费网络资源。</p>
<p>③ <strong>设置消息边界，也可以理解为分隔符</strong>，服务端从数据流中按消息边界分离出消息内容，一般使用换行符。</p>
<blockquote>
<ol>
<li><p>单看传输层，TCP是「面向字节流」传输的，本身是没有「包」的概念的，接收方将接收的报文段（segment）的数据提取出来按序放置在缓存中。</p>
</li>
<li><p>但是从应用层的角度看是有「包」的概念的，例如http&#x2F;1.1中采用了「管道(pipeline)」的思想，即可以多个http数据流「复用」同一个TCP，因此各个http数据流之间是「纠缠」的，那么就需要在应用层进行处理，「区分不同」的数据流，自然就需要处理「粘包」的问题。</p>
</li>
<li><p>个人认为TCP层没有「包」的概念反映了TCP层对应用层「多数据流」的支持较弱，这恰恰导致了粘包问题。而这种弱势也导致了HTTP&#x2F;3转向了QUIC，也就是基于UDP，自行实现更好的「多数据流复用&#x2F;分用」。</p>
</li>
</ol>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




    


<div class="post-block">
  
  

  <article itemscope itemtype="http://schema.org/Article" class="post-content" lang="">
    <link itemprop="mainEntityOfPage" href="http://example.com/2021/11/29/Redis%E5%AE%9E%E7%8E%B0%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="/images/avatar.gif">
      <meta itemprop="name" content="SongyangJi">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="JsyBlog">
      <meta itemprop="description" content="">
    </span>

    <span hidden itemprop="post" itemscope itemtype="http://schema.org/CreativeWork">
      <meta itemprop="name" content="undefined | JsyBlog">
      <meta itemprop="description" content="">
    </span>
      <header class="post-header">
        <h2 class="post-title" itemprop="name headline">
          <a href="/2021/11/29/Redis%E5%AE%9E%E7%8E%B0%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/" class="post-title-link" itemprop="url">Redis实现分布式锁</a>
        </h2>

        <div class="post-meta-container">
          <div class="post-meta">
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar"></i>
      </span>
      <span class="post-meta-item-text">发表于</span>

      <time title="创建时间：2021-11-29 11:00:11" itemprop="dateCreated datePublished" datetime="2021-11-29T11:00:11+08:00">2021-11-29</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-calendar-check"></i>
      </span>
      <span class="post-meta-item-text">更新于</span>
      <time title="修改时间：2022-09-02 22:44:06" itemprop="dateModified" datetime="2022-09-02T22:44:06+08:00">2022-09-02</time>
    </span>
    <span class="post-meta-item">
      <span class="post-meta-item-icon">
        <i class="far fa-folder"></i>
      </span>
      <span class="post-meta-item-text">分类于</span>
        <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
          <a href="/categories/Redis/" itemprop="url" rel="index"><span itemprop="name">Redis</span></a>
        </span>
    </span>

  
</div>

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">
          <h1 id="分布式锁简介"><a href="#分布式锁简介" class="headerlink" title="分布式锁简介"></a>分布式锁简介</h1><blockquote>
<p>分布式锁，是一种思想，它的实现方式有很多。比如，我们将沙滩当做分布式锁的组件，那么它看起来应该是这样的：</p>
<ul>
<li><strong>加锁</strong><br>在沙滩上踩一脚，留下自己的脚印，就对应了加锁操作。其他进程或者线程，看到沙滩上已经有脚印，证明锁已被别人持有，则等待。</li>
<li><strong>解锁</strong><br>把脚印从沙滩上抹去，就是解锁的过程。</li>
<li><strong>锁超时</strong><br>为了避免死锁，我们可以设置一阵风，在单位时间后刮起，将脚印自动抹去。</li>
</ul>
</blockquote>
<p>分布式锁的实现有很多，比如基于数据库、memcached、Redis、系统文件、zookeeper等。它们的核心的理念跟上面的过程大致相同。</p>
<p>在许多环境中不同进程必须以互斥方式使用共享资源进行操作时，分布式锁是一个非常有用的原语。此试图提供一种更规范的算法来实现Redis的分布式锁。我们提出了一种称为<code>Redlock</code>的算法，它实现了<code>DLM</code>（猜测是<code>Distributed Lock Manager</code>的缩写，分布式锁管理器），我们认为它比普通的单实例方法更安全。</p>
<h2 id="锁的安全和活性保证"><a href="#锁的安全和活性保证" class="headerlink" title="锁的安全和活性保证"></a>锁的安全和活性保证</h2><p>算法的三个核心特征（三大最低保证）：</p>
<ul>
<li><strong>Safety property</strong>（安全性）：互斥。确保在任何给定时刻下，只有一个客户端可以持有锁；</li>
<li><strong>Liveness property A</strong>（活性A）：无死锁。即使存在曾经锁定资源的客户端崩溃或者出现网络分区异常，确保锁总是能够成功获取；</li>
<li><strong>Liveness property B</strong>（活性B）：容错性。只要大多数Redis节点处于正常运行状态，客户端就可以获取和释放锁。</li>
</ul>
<h2 id="主从结构故障转移时的并发问题"><a href="#主从结构故障转移时的并发问题" class="headerlink" title="主从结构故障转移时的并发问题"></a>主从结构故障转移时的并发问题</h2><p>文档中还指出了目前算法对于故障转移的实现还存在明显的竞态条件问题（描述的应该是<code>Redis</code>主从架构下的问题）：</p>
<ul>
<li>客户端A获取Redis主节点中的锁（假设锁定的资源为X）</li>
<li>在Redis主节点把KEY同步到Redis从节点之前，Redis主节点崩溃</li>
<li>Redis从节点因为故障晋升为主节点</li>
<li>此时，客户端B获取资源X的锁成功，问题是资源X的锁在前面已经被客户端A获取过，这样就出现了并发问题。</li>
</ul>
<h1 id="单机下Redis实现锁"><a href="#单机下Redis实现锁" class="headerlink" title="单机下Redis实现锁"></a>单机下Redis实现锁</h1><p>单个实例是我们将用于此处描述的分布式算法的基础。</p>
<h2 id="锁的获取"><a href="#锁的获取" class="headerlink" title="锁的获取"></a>锁的获取</h2><p>下面的redis命令是获取锁的核心思想 —— 如果键不存在，则设置一个id值（此时相当于拿到了锁），并且为了保证上面所说的活性要求，还设置了键的过期时间。</p>
<figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">SET resource_name my_random_value NX PX 30000</span><br></pre></td></tr></table></figure>

<p>这实际上是<code>setnx</code>和<code>setex</code>的结合体，不过它是原子的。</p>
<p>如果较低版本的客户端中不支持这个命令，那就要使用lua脚本。</p>
<h2 id="锁的释放"><a href="#锁的释放" class="headerlink" title="锁的释放"></a>锁的释放</h2><p>下面的lua脚本的含义是：</p>
<p>当键存在，并且键值正是当前线程的独属id时（也就是防止释放别的线程加的锁，这就是这个独属于加到锁的线程的id的作用）。</p>
<figure class="highlight lua"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> redis.call(<span class="string">&quot;get&quot;</span>,KEYS[<span class="number">1</span>]) == ARGV[<span class="number">1</span>] <span class="keyword">then</span></span><br><span class="line">    <span class="keyword">return</span> redis.call(<span class="string">&quot;del&quot;</span>,KEYS[<span class="number">1</span>])</span><br><span class="line"><span class="keyword">else</span></span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line"><span class="keyword">end</span></span><br></pre></td></tr></table></figure>




<h2 id="使用Jedis实现”丐版“Redis锁"><a href="#使用Jedis实现”丐版“Redis锁" class="headerlink" title="使用Jedis实现”丐版“Redis锁"></a>使用Jedis实现”丐版“Redis锁</h2><p><del>注意下面的代码只能算是对redis加锁的原理予以初步的实践，远远达不到高性能、高可靠。</del></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">MyRedisLock</span> <span class="keyword">implements</span> <span class="title class_">Lock</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">JedisPool</span> <span class="variable">jedisPool</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">JedisPool</span>(<span class="string">&quot;localhost&quot;</span>);</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">EXPIRE_TIME_OUT</span> <span class="operator">=</span> <span class="number">1000</span>;</span><br><span class="line">    <span class="comment">// SET命令已经提供了可选的复合操作符</span></span><br><span class="line">    <span class="comment">// EX：设置超时时间，单位是秒</span></span><br><span class="line">    <span class="comment">// PX：设置超时时间，单位是毫秒</span></span><br><span class="line">    <span class="comment">// NX：IF NOT EXIST的缩写，只有KEY不存在的前提下才会设置K-V，设置成功返回1，否则返回0</span></span><br><span class="line">    <span class="comment">// XX：IF EXIST的缩写，只有在KEY存在的前提下才会设置K-V，设置成功返回1，否则返回0</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">SetParams</span> <span class="variable">params</span> <span class="operator">=</span> SetParams.setParams().nx().px(EXPIRE_TIME_OUT);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> ThreadLocal&lt;String&gt; lockedId = <span class="keyword">new</span> <span class="title class_">ThreadLocal</span>&lt;&gt;();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String lockName;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">MyRedisLock</span><span class="params">(String lockName)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.lockName = lockName;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> MyRedisLock <span class="title function_">getLock</span><span class="params">(String lockName)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">MyRedisLock</span>(lockName);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@SneakyThrows</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">lock</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">String</span> <span class="variable">id</span> <span class="operator">=</span> UUID.randomUUID().toString();</span><br><span class="line">        lockedId.set(id);</span><br><span class="line">        <span class="keyword">try</span> (<span class="type">Jedis</span> <span class="variable">jedis</span> <span class="operator">=</span> jedisPool.getResource()) &#123;</span><br><span class="line">            <span class="comment">// 加锁失败就重试</span></span><br><span class="line">            <span class="keyword">for</span> (; ; ) &#123;</span><br><span class="line">                <span class="type">String</span> <span class="variable">statusCode</span> <span class="operator">=</span> jedis.set(lockName, id, params);</span><br><span class="line">                <span class="keyword">if</span> (<span class="string">&quot;OK&quot;</span>.equals(statusCode)) &#123;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                Thread.sleep(<span class="number">100</span>); <span class="comment">// 可以设计成 wait-notify 范式，不过这里就简陋一点了</span></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">unlock</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> (<span class="type">Jedis</span> <span class="variable">jedis</span> <span class="operator">=</span> jedisPool.getResource()) &#123;</span><br><span class="line">            <span class="comment">// 为做到原子性，使用 Lua 脚本</span></span><br><span class="line">            <span class="type">String</span> <span class="variable">script</span> <span class="operator">=</span></span><br><span class="line">                    <span class="string">&quot;if redis.call(&#x27;get&#x27;,KEYS[1]) == ARGV[1] then&quot;</span> +</span><br><span class="line">                            <span class="string">&quot;   return redis.call(&#x27;del&#x27;,KEYS[1]) &quot;</span> +</span><br><span class="line">                            <span class="string">&quot;else&quot;</span> +</span><br><span class="line">                            <span class="string">&quot;   return 0 &quot;</span> +</span><br><span class="line">                            <span class="string">&quot;end&quot;</span>;</span><br><span class="line">            <span class="type">String</span> <span class="variable">id</span> <span class="operator">=</span> lockedId.get();</span><br><span class="line">            jedis.eval(script, Collections.singletonList(lockName), Collections.singletonList(id)).toString();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">  </span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">lockInterruptibly</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">tryLock</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">tryLock</span><span class="params">(<span class="type">long</span> time, TimeUnit unit)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> Condition <span class="title function_">newCondition</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">// 测试代码</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line">        <span class="type">MyRedisLock</span> <span class="variable">lock</span> <span class="operator">=</span> MyRedisLock.getLock(<span class="string">&quot;lock1&quot;</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//        MyInteger.testLock(lock);</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="type">long</span> <span class="variable">l</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        <span class="keyword">final</span> <span class="type">int</span> <span class="variable">N</span> <span class="operator">=</span> <span class="number">10000</span>;</span><br><span class="line">        <span class="type">MyInteger</span> <span class="variable">integer</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MyInteger</span>(N);</span><br><span class="line">        <span class="type">CountDownLatch</span> <span class="variable">countDownLatch</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatch</span>(N);</span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">pool</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">10</span>);</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>; i &lt; N; i++) &#123;</span><br><span class="line">            pool.submit(() -&gt; &#123;</span><br><span class="line">                lock.lock();</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    integer.decr();</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    lock.unlock();</span><br><span class="line">                &#125;</span><br><span class="line">                countDownLatch.countDown();</span><br><span class="line">            &#125;);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            countDownLatch.await(); <span class="comment">// just wait all tasks to finish</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">&quot;end : &quot;</span> + integer.getX());</span><br><span class="line">        <span class="type">long</span> <span class="variable">r</span> <span class="operator">=</span> System.currentTimeMillis();</span><br><span class="line">        System.out.println(<span class="string">&quot;耗时 &quot;</span> + (r - l) + <span class="string">&quot; ms&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>


<p>上述算法的主要缺点：</p>
<ol>
<li>忙等（busy-waiting）的问题，消耗CPU资源；</li>
<li>只适用于单机；</li>
<li>不可重入</li>
<li>没有限制等待获取的锁的超时时间</li>
<li>不能做到在等待锁的时候响应中断</li>
</ol>
<h1 id="Redisson实现分布式锁"><a href="#Redisson实现分布式锁" class="headerlink" title="Redisson实现分布式锁"></a>Redisson实现分布式锁</h1><p><a target="_blank" rel="noopener" href="https://redisson.org/">Redisson</a>是架设在<a target="_blank" rel="noopener" href="http://redis.cn/">Redis</a>基础上的一个Java驻内存数据网格（In-Memory Data Grid）。</p>
<p>相对于Jedis而言，Redisson是一个更高级的客户端。</p>
<p>它里面实现了各种分布式锁（本文只介绍和使用简单的）。</p>
<h2 id="使用Redisson中的锁"><a href="#使用Redisson中的锁" class="headerlink" title="使用Redisson中的锁"></a>使用Redisson中的锁</h2><p><strong>maven依赖</strong></p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>org.redisson<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>redisson<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.16.4<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure>


<p><strong>简单使用 lock</strong></p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">  <span class="comment">// 配置类</span></span><br><span class="line">  <span class="type">Config</span> <span class="variable">config</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Config</span>();</span><br><span class="line">  config.useSingleServer().setAddress(<span class="string">&quot;redis://127.0.0.1:6379&quot;</span>);</span><br><span class="line">  config.useSingleServer().setPassword(<span class="string">&quot;redis1234&quot;</span>);</span><br><span class="line">  <span class="comment">// 初始化客户端</span></span><br><span class="line">  <span class="keyword">final</span> <span class="type">RedissonClient</span> <span class="variable">client</span> <span class="operator">=</span> Redisson.create(config);  </span><br><span class="line">  <span class="type">RLock</span> <span class="variable">lock</span> <span class="operator">=</span> client.getLock(<span class="string">&quot;lock1&quot;</span>);</span><br><span class="line">  </span><br><span class="line">  <span class="comment">// 加锁</span></span><br><span class="line">lock.lock();</span><br><span class="line">  <span class="keyword">try</span>&#123;</span><br><span class="line">      <span class="comment">// do something ...  </span></span><br><span class="line">  &#125;<span class="keyword">finally</span>&#123;</span><br><span class="line">      <span class="comment">// 释放锁</span></span><br><span class="line">      lock.unlock();</span><br><span class="line">  &#125;</span><br></pre></td></tr></table></figure>



<h2 id="Redisson中RLock的实现特点"><a href="#Redisson中RLock的实现特点" class="headerlink" title="Redisson中RLock的实现特点"></a>Redisson中RLock的实现特点</h2><p><code>Redisson</code>中<code>RLock</code>的实现是基本参照了<code>Redis</code>的<code>red lock</code>算法进行实现，不过在原始的<code>red lock</code>算法下进行了改良，主要包括下面的特性：</p>
<ul>
<li>互斥</li>
<li>无死锁</li>
<li>可重入，类似于<code>ReentrantLock</code>，同一个线程可以重复获取同一个资源的锁（一般使用计数器实现），锁的重入特性一般情况下有利于提高资源的利用率</li>
<li><strong>续期</strong>，这个是一个比较前卫解决思路，也就是如果一个客户端对资源<code>X</code>永久锁定，那么并不是直接对<code>KEY</code>生存周期设置为<code>-1</code>，而是通<strong>过一个守护线程每隔固定周期延长<code>KEY</code>的过期时间</strong>，这样就能实现<strong>在守护线程不被杀掉的前提下，避免客户端崩溃导致锁无法释放长期占用资源的问题</strong></li>
<li><strong>锁状态变更订阅</strong>，依赖于<code>org.redisson.pubsub.LockPubSub</code>，用于订阅和通知锁释放事件</li>
<li>数据类型选用了<code>HASH</code>，配合<code>Lua</code>脚本完成多个命令的原子性</li>
</ul>
<p><code>RLock</code>的类图</p>
<p><img src="/2021/11/29/Redis%E5%AE%9E%E7%8E%B0%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/rlock.jpeg"></p>
<p>RedissonLock<code>就是</code>RLock<code>的直接实现，也是分布式锁实现的核心类，从源码中看到</code>Redisson.getLock()<code>就是直接实例化</code>RedissonLock。</p>
<p><code>RedissonLock</code>的类继承图</p>
<p><img src="/2021/11/29/Redis%E5%AE%9E%E7%8E%B0%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/redission-lock.jpeg"></p>
<h2 id="加锁整体流程"><a href="#加锁整体流程" class="headerlink" title="加锁整体流程"></a>加锁整体流程</h2><blockquote>
<p>下面的代码是版本3.16.4中的，其他版本有点差别：</p>
</blockquote>
<h3 id="构造锁"><a href="#构造锁" class="headerlink" title="构造锁"></a>构造锁</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_">RedissonLock</span><span class="params">(CommandAsyncExecutor commandExecutor, String name)</span> &#123;</span><br><span class="line">    <span class="built_in">super</span>(commandExecutor, name);</span><br><span class="line">    <span class="comment">//命令执行器</span></span><br><span class="line">    <span class="built_in">this</span>.commandExecutor = commandExecutor;</span><br><span class="line">    <span class="comment">//UUID字符串</span></span><br><span class="line">    <span class="built_in">this</span>.id = commandExecutor.getConnectionManager().getId();</span><br><span class="line">    <span class="comment">//内部锁过期时间(默认是 30*1000 , 也就是 30 秒)</span></span><br><span class="line">    <span class="built_in">this</span>.internalLockLeaseTime = commandExecutor.</span><br><span class="line">                getConnectionManager().getCfg().getLockWatchdogTimeout();</span><br><span class="line">    <span class="built_in">this</span>.entryName = id + <span class="string">&quot;:&quot;</span> + name;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><code>lockWatchdogTimeout</code>参数只有在没有使用<code>leaseTimeout</code>参数定义的成功获取到锁的场景（简单来说就是不设置时限的加锁）下生效，<br>如果看门狗在下一个<code>lockWatchdogTimeout</code>周期内不进行续期，那么锁就会过期释放<br>（从源码上看，每三分之一<code>lockWatchdogTimeout</code>就会执行一次续期任务，每次通过<code>pexpire</code>把<code>KEY</code>的存活周期延长<code>lockWatchdogTimeout</code>），<code>lockWatchdogTimeout</code>的默认值为<code>30000</code>，也就是<code>30</code>秒。</p>
<p>这里先列举一下<code>RedissonLock</code>中获取名称的方法，以便后面分析这些名称作为<code>K-V</code>结构的<code>KEY</code>时候使用：</p>
<ul>
<li><code>id</code>：由配置实例化时候实例化的<code>UUID</code>实例生成，从源码上分析每个连接方式的<code>Redisson</code>实例有唯一的<code>UUID</code>，<code>ConnectionManager</code>初始化的时候会调用<code>UUID id = UUID.randomUUID()</code>，笔者认为可以理解为<code>Redisson</code>实例在某个应用程序进程中的唯一标识，毕竟一般情况下，一个应用程序应该只会应用一种<code>Redisson</code>的连接方式</li>
<li><code>getEntryName()</code>：返回的是<code>UUID + : + $KEY</code>，例如<code>559cc9df-bad8-4f6c-86a4-ffa51b7f1c36:resource:x</code></li>
<li><code>getName()</code>：返回的是<code>$KEY</code>，例如<code>resource:x</code></li>
<li><code>getChannelName()</code>：返回的是<code>redisson_lock__channel:&#123;$KEY&#125;</code>，例如<code>redisson_lock__channel:&#123;resource:x&#125;</code></li>
<li><code>getLockName(long threadId)</code>：返回的是<code>UUID + : + $threadId</code>，例如<code>559cc9df-bad8-4f6c-86a4-ffa51b7f1c36:1</code></li>
</ul>
<h3 id="加锁"><a href="#加锁" class="headerlink" title="加锁"></a>加锁</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line">    <span class="comment">// 获取锁</span></span><br><span class="line">		<span class="keyword">public</span> RLock <span class="title function_">getLock</span><span class="params">(String name)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RedissonLock</span>(commandExecutor, name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">		<span class="comment">/**</span></span><br><span class="line"><span class="comment">   	 * LeaseTime - 获取后持有锁的最长时间，如果它尚未通过调用unlock 。 如果leaseTime 为-1，则保持锁定直到显式解锁。</span></span><br><span class="line"><span class="comment">   	 */</span></span><br><span class="line">		 <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">lock</span><span class="params">(<span class="type">long</span> leaseTime, TimeUnit unit, <span class="type">boolean</span> interruptibly)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line">      	<span class="comment">// 当前线程ID</span></span><br><span class="line">        <span class="type">long</span> <span class="variable">threadId</span> <span class="operator">=</span> Thread.currentThread().getId();</span><br><span class="line">      	<span class="comment">// 尝试获取锁</span></span><br><span class="line">        <span class="type">Long</span> <span class="variable">ttl</span> <span class="operator">=</span> tryAcquire(-<span class="number">1</span>, leaseTime, unit, threadId);</span><br><span class="line">        <span class="comment">// lock acquired</span></span><br><span class="line">        <span class="comment">// 如果ttl为 null，则证明获取锁成功</span></span><br><span class="line">        <span class="keyword">if</span> (ttl == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">      	<span class="comment">// 如果获取锁失败，则订阅到对应这个锁的channel</span></span><br><span class="line">        RFuture&lt;RedissonLockEntry&gt; future = subscribe(threadId);</span><br><span class="line">        <span class="keyword">if</span> (interruptibly) &#123;</span><br><span class="line">            commandExecutor.syncSubscriptionInterrupted(future);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            commandExecutor.syncSubscription(future);</span><br><span class="line">        &#125;</span><br><span class="line">       </span><br><span class="line">        <span class="comment">// 不断尝试</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">                <span class="comment">// 再次尝试获取锁</span></span><br><span class="line">                ttl = tryAcquire(-<span class="number">1</span>, leaseTime, unit, threadId);</span><br><span class="line">                <span class="comment">// lock acquired</span></span><br><span class="line">                <span class="keyword">if</span> (ttl == <span class="literal">null</span>) &#123;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// waiting for message</span></span><br><span class="line">                <span class="comment">// ttl大于0 则等待ttl时间后继续尝试获取</span></span><br><span class="line">                <span class="keyword">if</span> (ttl &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);</span><br><span class="line">                    &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">                        <span class="keyword">if</span> (interruptibly) &#123;</span><br><span class="line">                            <span class="keyword">throw</span> e;</span><br><span class="line">                        &#125;</span><br><span class="line">                        future.getNow().getLatch().tryAcquire(ttl, TimeUnit.MILLISECONDS);</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="keyword">if</span> (interruptibly) &#123;</span><br><span class="line">                        future.getNow().getLatch().acquire();</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                        future.getNow().getLatch().acquireUninterruptibly();</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="comment">// 取消对channel的订阅</span></span><br><span class="line">            unsubscribe(future, threadId);</span><br><span class="line">        &#125;</span><br><span class="line"><span class="comment">//        get(lockAsync(leaseTime, unit));</span></span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>



<p>如上代码，就是加锁的全过程。先调用<code>tryAcquire</code>来获取锁，如果返回值ttl为空，则证明加锁成功，返回；如果不为空，则证明加锁失败。这时候，它会订阅这个锁的Channel，等待锁释放的消息，然后重新尝试获取锁。流程如下：</p>
<img src="lock1.webp" style="zoom:60%;" />











<p>获取锁的过程是怎样的呢？接下来就要看<code>tryAcquire</code>方法。在这里，它有两种处理方式，一种是带有过期时间的锁，一种是不带过期时间的锁。</p>
<h3 id="获取互斥状态"><a href="#获取互斥状态" class="headerlink" title="获取互斥状态"></a>获取互斥状态</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">    <span class="comment">// 同步方法依赖异步方法</span></span><br><span class="line">    <span class="keyword">private</span> Long <span class="title function_">tryAcquire</span><span class="params">(<span class="type">long</span> waitTime, <span class="type">long</span> leaseTime, TimeUnit unit, <span class="type">long</span> threadId)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> get(tryAcquireAsync(waitTime, leaseTime, unit, threadId));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 通过传入锁持有的最大时间和线程ID异步获取锁</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">private</span> &lt;T&gt; RFuture&lt;Long&gt; <span class="title function_">tryAcquireAsync</span><span class="params">(<span class="type">long</span> waitTime, <span class="type">long</span> leaseTime, TimeUnit unit, <span class="type">long</span> threadId)</span> &#123;</span><br><span class="line">        RFuture&lt;Long&gt; ttlRemainingFuture;</span><br><span class="line">  </span><br><span class="line">  </span><br><span class="line">        <span class="keyword">if</span> (leaseTime != -<span class="number">1</span>) &#123;   <span class="comment">// 锁持有最大时间不为-1，也就是明确锁的持有时间，不是永久持有的场景</span></span><br><span class="line">            ttlRemainingFuture = tryLockInnerAsync(waitTime, leaseTime, unit, threadId, RedisCommands.EVAL_LONG);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 先按照30秒的过期时间来执行获取锁的方法</span></span><br><span class="line">            ttlRemainingFuture = tryLockInnerAsync(waitTime, internalLockLeaseTime,</span><br><span class="line">                    TimeUnit.MILLISECONDS, threadId, RedisCommands.EVAL_LONG);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 如果还持有这个锁，则开启定时任务不断刷新该锁的过期时间</span></span><br><span class="line">        ttlRemainingFuture.onComplete((ttlRemaining, e) -&gt; &#123;</span><br><span class="line">            <span class="comment">// // 执行异常场景直接返回</span></span><br><span class="line">            <span class="keyword">if</span> (e != <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// lock acquired（当前已经获取到锁）</span></span><br><span class="line">            <span class="keyword">if</span> (ttlRemaining == <span class="literal">null</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (leaseTime != -<span class="number">1</span>) &#123;</span><br><span class="line">                    internalLockLeaseTime = unit.toMillis(leaseTime);</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="comment">// 如果是永久持有，定时调度进行 续期操作</span></span><br><span class="line">                    scheduleExpirationRenewal(threadId);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line">        <span class="keyword">return</span> ttlRemainingFuture;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure>





<h4 id="底层加锁逻辑"><a href="#底层加锁逻辑" class="headerlink" title="底层加锁逻辑"></a>底层加锁逻辑</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">&lt;T&gt; RFuture&lt;T&gt; <span class="title function_">tryLockInnerAsync</span><span class="params">(<span class="type">long</span> waitTime, <span class="type">long</span> leaseTime, TimeUnit unit, <span class="type">long</span> threadId, RedisStrictCommand&lt;T&gt; command)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> evalWriteAsync(getRawName(), LongCodec.INSTANCE, command,</span><br><span class="line">             <span class="comment">//如果锁不存在，则通过hincrby设置持有数为 1 ，并设置过期时间</span></span><br><span class="line">             <span class="comment">// KEYS[1] ：hash类型的键，代表哪个锁</span></span><br><span class="line">             <span class="comment">// ARGV[2] ：hash类型的字段，代表目前获取到锁的线程名</span></span><br><span class="line">             <span class="comment">// ARGV[1] ：传入的锁的过期时间                       </span></span><br><span class="line">            <span class="string">&quot;if (redis.call(&#x27;exists&#x27;, KEYS[1]) == 0) then &quot;</span> +</span><br><span class="line">                    <span class="string">&quot;redis.call(&#x27;hincrby&#x27;, KEYS[1], ARGV[2], 1); &quot;</span> +</span><br><span class="line">                    <span class="string">&quot;redis.call(&#x27;pexpire&#x27;, KEYS[1], ARGV[1]); &quot;</span> +</span><br><span class="line">                    <span class="string">&quot;return nil; &quot;</span> +</span><br><span class="line">                    <span class="string">&quot;end; &quot;</span> +</span><br><span class="line">                    <span class="comment">// 如果锁已存在，并且锁的是当前线程，则通过hincrby给数值递增1</span></span><br><span class="line">                    <span class="string">&quot;if (redis.call(&#x27;hexists&#x27;, KEYS[1], ARGV[2]) == 1) then &quot;</span> +</span><br><span class="line">                    <span class="string">&quot;redis.call(&#x27;hincrby&#x27;, KEYS[1], ARGV[2], 1); &quot;</span> +</span><br><span class="line">                    <span class="string">&quot;redis.call(&#x27;pexpire&#x27;, KEYS[1], ARGV[1]); &quot;</span> +</span><br><span class="line">                    <span class="string">&quot;return nil; &quot;</span> +</span><br><span class="line">                    <span class="string">&quot;end; &quot;</span> +</span><br><span class="line">                    <span class="string">&quot;return redis.call(&#x27;pttl&#x27;, KEYS[1]);&quot;</span>, <span class="comment">// //如果锁已存在，但并非本线程，则返回过期时间ttl</span></span><br><span class="line">            Collections.singletonList(getRawName(), unit.toMillis(leaseTime), getLockName(threadId));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<p>这段LUA代码看起来并不复杂，有三个判断：</p>
<ul>
<li><strong>通过exists判断，如果锁不存在，则设置值和过期时间，加锁成功</strong></li>
<li><strong>通过hexists判断，如果锁已存在，并且锁的是当前线程，则证明是重入锁，加锁成功</strong></li>
<li><strong>如果锁已存在，但锁的不是当前线程，则证明有其他线程持有锁。返回当前锁的过期时间，加锁失败</strong></li>
</ul>
<img src="lock2.webp" style="zoom:60%;" />





<p>最后再概括一下<code>Redisson</code>中实现<code>red lock</code>算法使用的<code>HASH</code>数据类型：</p>
<ul>
<li><code>KEY</code>代表的就是资源或者锁，<strong>创建、存在性判断，延长生存周期和删除操作总是针对<code>KEY</code>进行的</strong></li>
<li><code>FIELD</code>代表的是锁名称<code>lockName()</code>，但是其实它由<code>Redisson</code>连接管理器实例的初始化<code>UUID</code>拼接客户端线程<code>ID</code>组成，严格来说应该是获取锁的客户端线程唯一标识；</li>
<li><code>VALUE</code>代表的是客户端线程对于锁的持有量。</li>
</ul>
<h2 id="解锁整体流程"><a href="#解锁整体流程" class="headerlink" title="解锁整体流程"></a>解锁整体流程</h2><h3 id="解锁"><a href="#解锁" class="headerlink" title="解锁"></a>解锁</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> RFuture&lt;Void&gt; <span class="title function_">unlockAsync</span><span class="params">(<span class="type">long</span> threadId)</span> &#123;</span><br><span class="line">    RPromise&lt;Void&gt; result = <span class="keyword">new</span> <span class="title class_">RedissonPromise</span>&lt;&gt;();</span><br><span class="line">    <span class="comment">// // 返回的RFuture如果持有的结果为true，说明解锁成功，返回NULL说明线程ID异常，加锁和解锁的客户端线程不是同一个线程</span></span><br><span class="line">    RFuture&lt;Boolean&gt; future = unlockInnerAsync(threadId);</span><br><span class="line">    future.onComplete((opStatus, e) -&gt; &#123;</span><br><span class="line">      	<span class="comment">// 需要取消看门狗的续期任务</span></span><br><span class="line">        cancelExpirationRenewal(threadId);</span><br><span class="line">		<span class="comment">// 这是内部的异常，说明解锁异常</span></span><br><span class="line">        <span class="keyword">if</span> (e != <span class="literal">null</span>) &#123;</span><br><span class="line">            result.tryFailure(e);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">		<span class="comment">// 这种情况说明线程ID异常，加锁和解锁的客户端线程不是同一个线程，抛出IllegalMonitorStateException异常</span></span><br><span class="line">        <span class="keyword">if</span> (opStatus == <span class="literal">null</span>) &#123;</span><br><span class="line">            <span class="type">IllegalMonitorStateException</span> <span class="variable">cause</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">IllegalMonitorStateException</span>(<span class="string">&quot;attempt to unlock lock, not locked by current thread by node id: &quot;</span></span><br><span class="line">                    + id + <span class="string">&quot; thread-id: &quot;</span> + threadId);</span><br><span class="line">            result.tryFailure(cause);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">		<span class="comment">// 走到这里说明正常解锁</span></span><br><span class="line">        result.trySuccess(<span class="literal">null</span>);</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<h3 id="解锁的底层逻辑"><a href="#解锁的底层逻辑" class="headerlink" title="解锁的底层逻辑"></a>解锁的底层逻辑</h3><p>核心还是lua脚本实现的原子性</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> RFuture&lt;Boolean&gt; <span class="title function_">unlockInnerAsync</span><span class="params">(<span class="type">long</span> threadId)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> evalWriteAsync(getRawName(), LongCodec.INSTANCE, RedisCommands.EVAL_BOOLEAN,</span><br><span class="line">            <span class="comment">// KEYS[1] 锁名</span></span><br><span class="line">            <span class="comment">// ARGV[3] 线程id</span></span><br><span class="line">            <span class="comment">// 如果释放锁的线程和已存在锁的线程不是同一个线程，返回null         </span></span><br><span class="line">            <span class="string">&quot;if (redis.call(&#x27;hexists&#x27;, KEYS[1], ARGV[3]) == 0) then &quot;</span> +</span><br><span class="line">                    <span class="string">&quot;return nil;&quot;</span> +</span><br><span class="line">                    <span class="string">&quot;end; &quot;</span> +</span><br><span class="line">                    <span class="comment">// 通过hincrby递减1的方式，释放一次锁</span></span><br><span class="line">                    <span class="string">&quot;local counter = redis.call(&#x27;hincrby&#x27;, KEYS[1], ARGV[3], -1); &quot;</span> +</span><br><span class="line">                    <span class="comment">// 若剩余次数大于0 ，则刷新过期时间</span></span><br><span class="line">                    <span class="string">&quot;if (counter &gt; 0) then &quot;</span> +</span><br><span class="line">                    <span class="comment">// ARGV[2] 过期时间</span></span><br><span class="line">                    <span class="string">&quot;redis.call(&#x27;pexpire&#x27;, KEYS[1], ARGV[2]); &quot;</span> +</span><br><span class="line">                    <span class="string">&quot;return 0; &quot;</span> +</span><br><span class="line">                    <span class="string">&quot;else &quot;</span> +</span><br><span class="line">                    <span class="comment">// 否则证明锁已经释放，删除key并发布锁释放的消息</span></span><br><span class="line">                    <span class="string">&quot;redis.call(&#x27;del&#x27;, KEYS[1]); &quot;</span> +</span><br><span class="line">                    <span class="comment">// KEYS[2] ： 指的是 publish 的频道名</span></span><br><span class="line">                    <span class="comment">// ARGV[1] ：解锁的消息</span></span><br><span class="line">                    <span class="string">&quot;redis.call(&#x27;publish&#x27;, KEYS[2], ARGV[1]); &quot;</span> +</span><br><span class="line">                    <span class="string">&quot;return 1; &quot;</span> +</span><br><span class="line">                    <span class="string">&quot;end; &quot;</span> +</span><br><span class="line">                    <span class="string">&quot;return nil;&quot;</span>,</span><br><span class="line">            Arrays.asList(getRawName(), getChannelName()), LockPubSub.UNLOCK_MESSAGE, internalLockLeaseTime, getLockName(threadId));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>



<ol>
<li><p><strong>如果解锁的线程和当前锁的线程不是同一个，解锁失败，抛出异常</strong>‘’</p>
</li>
<li><p><strong>通过hincrby递减1，先释放一次锁。</strong></p>
</li>
<li><p><strong>若剩余次数还大于0，则证明当前锁是重入锁，刷新过期时间；</strong></p>
</li>
<li><p><strong>若剩余次数小于0，删除key并发布锁释放的消息，解锁成功</strong></p>
</li>
</ol>
<img src="unlock.webp" style="zoom:60%;" />






<h2 id="Redisson的锁总结"><a href="#Redisson的锁总结" class="headerlink" title="Redisson的锁总结"></a>Redisson的锁总结</h2><p><code>Redisson</code>中的<code>red lock</code>实现，应用到下面的核心技术：</p>
<ul>
<li>合理应用<code>Redis</code>的基本数据类型<code>HASH</code></li>
<li><code>Redis</code>的订阅发布</li>
<li><code>Lua</code>脚本的原子性</li>
<li><code>Netty</code>中的<code>Promise</code>实现</li>
<li><code>Netty</code>中的时间轮<code>HashedWheelTimer</code>和对应的定时任务<code>(HashedWheel)Timeout</code></li>
<li><code>Semaphore</code>进行带期限、永久或者可中断的阻塞以及唤醒，替代<code>CountDownLatch</code>中的无等待期限阻塞</li>
</ul>
<p>上面的核心技术相对合理地应用，才能实现一个高效而且容错能力相对比较高的分布式锁方案，但是从目前来看，<code>Redisson</code>仍未解决<code>red lock</code>算法中的故障转移缺陷，笔者认为这个有可能是<code>Redis</code>实现分布式锁方案的一个底层缺陷，<strong>此方案在<code>Redis</code>单实例中是相对完善</strong>，一旦应用在<code>Redis</code>集群（普通主从、哨兵或者<code>Cluster</code>），有几率会出现前文提到的节点角色切换导致多个不同客户端获取到同一个资源对应的锁的问题。暂时无解。</p>
<blockquote>
<p>但值得注意的是，上面的两种实现方式都是针对单机Redis实例而进行的。如果我们有多个Redis实例，请参阅<strong>Redlock算法</strong>。该算法的具体内容，请参考<a target="_blank" rel="noopener" href="http://redis.cn/topics/distlock.html">http://redis.cn/topics/distlock.html</a></p>
</blockquote>
<blockquote>
<p><a target="_blank" rel="noopener" href="https://www.cnblogs.com/throwable/p/14264804.html">冷饭新炒：理解Redisson中分布式锁的实现 </a></p>
<p><a target="_blank" rel="noopener" href="https://redis.io/topics/distlock">Distributed locks with Redis</a></p>
<p><a target="_blank" rel="noopener" href="https://blog.csdn.net/dlf123321/article/details/123032170?spm=1001.2014.3001.5501">分布式锁的进化过程</a></p>
</blockquote>

      
    </div>

    
    
    

    <footer class="post-footer">
        <div class="post-eof"></div>
      
    </footer>
  </article>
</div>




  <nav class="pagination">
    <a class="extend prev" rel="prev" title="上一页" aria-label="上一页" href="/page/16/"><i class="fa fa-angle-left"></i></a><a class="page-number" href="/">1</a><span class="space">&hellip;</span><a class="page-number" href="/page/16/">16</a><span class="page-number current">17</span><a class="page-number" href="/page/18/">18</a><span class="space">&hellip;</span><a class="page-number" href="/page/26/">26</a><a class="extend next" rel="next" title="下一页" aria-label="下一页" href="/page/18/"><i class="fa fa-angle-right"></i></a>
  </nav>

</div>
  </main>

  <footer class="footer">
    <div class="footer-inner">


<div class="copyright">
  &copy; 
  <span itemprop="copyrightYear">2023</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">SongyangJi</span>
</div>
  <div class="powered-by">由 <a href="https://hexo.io/" rel="noopener" target="_blank">Hexo</a> & <a href="https://theme-next.js.org/muse/" rel="noopener" target="_blank">NexT.Muse</a> 强力驱动
  </div>

    </div>
  </footer>

  
  <div class="toggle sidebar-toggle" role="button">
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
    <span class="toggle-line"></span>
  </div>
  <div class="sidebar-dimmer"></div>
  <div class="back-to-top" role="button" aria-label="返回顶部">
    <i class="fa fa-arrow-up fa-lg"></i>
    <span>0%</span>
  </div>

<noscript>
  <div class="noscript-warning">Theme NexT works best with JavaScript enabled</div>
</noscript>


  
  <script src="https://cdnjs.cloudflare.com/ajax/libs/animejs/3.2.1/anime.min.js" integrity="sha256-XL2inqUJaslATFnHdJOi9GfQ60on8Wx1C2H8DYiN1xY=" crossorigin="anonymous"></script>
<script src="/js/comments.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/muse.js"></script><script src="/js/next-boot.js"></script>

  <script src="https://cdnjs.cloudflare.com/ajax/libs/hexo-generator-searchdb/1.4.1/search.js" integrity="sha256-1kfA5uHPf65M5cphT2dvymhkuyHPQp5A53EGZOnOLmc=" crossorigin="anonymous"></script>
<script src="/js/third-party/search/local-search.js"></script>





  





</body>
</html>
